So basically this is what I'm trying to achieve
WITH THE WIDGET WITH THE RED X BEING THE ROOT WIDGET IN WHICH ALL WIDGETS NEEDS TO BE BUILT AROUND IT
All the widgets here are going to be 'rendered' dynamically which means I get their informations from somewhere, so to achieve this I thought of two options:
Widget panelWidget(Panel panel) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//WIDGETS ON THE LEFT
...panel.attachedPanels
.where((element) => element.attachedToSide == 3)
.map(
(e) => Container(
height: getH(e.panelHeight),
width: getW(e.panelWidth),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
),
)
.toList(),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//WIDGETS ON TOP
...panel.attachedPanels
.where((element) => element.attachedToSide == 2)
.map(
(e) => Container(
height: getH(e.panelHeight),
width: getW(e.panelWidth),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
),
)
.toList(),
Center( //THIS IS THE ROOT WIDGET
child: Container(
height: getH(panel.panelHeight),
width: getW(panel.panelWidth),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
),
),
//WIDGETS ON THE BOTTOM
...panel.attachedPanels
.where((element) => element.attachedToSide == 0)
.map(
(e) => Container(
height: getH(e.panelHeight),
width: getW(e.panelWidth),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
),
)
.toList(),
],
),
//WIDGETS ON THE RITGHT
...panel.attachedPanels
.where((element) => element.attachedToSide == 1)
.map(
(e) => Container(
height: getH(e.panelHeight),
width: getW(e.panelWidth),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
),
)
.toList(),
],
);
}
please mind the logic as I'm focused now on figuring this out first, you can see bellow the root widget commented and around it from 4 sides there is the list of the widgets I need to render, on paper it looks fine but this is the output, I'm using Flutter Web :
As you can see the left and right widgets are NOT aligned with the root widget because they are following the Row's cross axis alignment. Flutter is doing it's job of putting these 2 widgets at the center but the widgets don't have the same size thus shifting the center point a bit for the whole widget tree, I tried different combinations but they all did not work, Any help is highly welcomed
Use the Table
widget, which does what you're trying to do for you.
Don't think of building a widget off to the side of another "root" widget. Think of everything in terms of a tree where everything has a parent-child/children relationship. Each of your widgets is at the same level in the tree, they just need the right parent to put them in the right place.
Your desired layout using Placeholder
s in the Table
is implemented in the following code. It can be copy-pasted into DartPad.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow>[
TableRow(
children: <Widget>[
const SizedBox(),
SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
const SizedBox(),
],
),
TableRow(
children: <Widget>[
SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
],
),
TableRow(
children: <Widget>[
const SizedBox(),
SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
const SizedBox(),
],
),
],
);
}
}
For accommodating a "dynamic" layout, the changes to the above code are trivial. The following adjusts MyWidget
to take optional top, bottom, left, and right widgets and adjusts everything accordingly so that the Table
works correctly.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(
root: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
top: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
right: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
final Widget root;
final Widget? right, left, top, bottom;
const MyWidget({
required this.root,
this.right,
this.left,
this.top,
this.bottom,
});
@override
Widget build(BuildContext context) {
final List<Widget> middleRow = [
if(left != null)
left!,
root,
if(right != null)
right!,
];
final List<Widget> topRow = [
if(left != null)
const SizedBox(),
top ?? const SizedBox(),
if(right != null)
const SizedBox(),
];
final List<Widget> bottomRow = [
if(left != null)
const SizedBox(),
bottom ?? const SizedBox(),
if(right != null)
const SizedBox(),
];
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow>[
TableRow(
children: topRow,
),
TableRow(
children: middleRow,
),
TableRow(
children: bottomRow,
),
],
);
}
}
Or if you want empty space where widgets are omitted:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(
root: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
top: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
right: SizedBox(
height: 100,
child: Placeholder(color: Colors.white),
),
),
),
),
);
}
}
class MyWidget extends StatelessWidget {
final Widget root;
final Widget? right, left, top, bottom;
const MyWidget({
required this.root,
this.right,
this.left,
this.top,
this.bottom,
});
@override
Widget build(BuildContext context) {
final List<Widget> middleRow = [
left ?? const SizedBox(),
root,
right ?? const SizedBox(),
];
final List<Widget> topRow = [
const SizedBox(),
top ?? const SizedBox(),
const SizedBox(),
];
final List<Widget> bottomRow = [
const SizedBox(),
bottom ?? const SizedBox(),
const SizedBox(),
];
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow>[
TableRow(
children: topRow,
),
TableRow(
children: middleRow,
),
TableRow(
children: bottomRow,
),
],
);
}
}