Flutter: Understanding how does a Stateless/Statefull widget layout its child?
You have a point. I'd say my article is imprecise in that regard.
A widget doesn't need to create a RenderObject
. It can, instead, use a composition of other widgets that create RenderObjects
themselves.
If a widget is a composition of other widgets, then instead of looking at the performLayout
you can simply look at that widget's build
method to see what it's doing. In the case of a Container
, this is its build
method:
Widget build(BuildContext context) {
Widget? current = child;
if (child == null && (constraints == null || !constraints!.isTight)) {
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
if (alignment != null)
current = Align(alignment: alignment!, child: current);
final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
if (color != null)
current = ColoredBox(color: color!, child: current);
if (clipBehavior != Clip.none) {
assert(decoration != null);
current = ClipPath(
clipper: _DecorationClipper(
textDirection: Directionality.maybeOf(context),
decoration: decoration!,
),
clipBehavior: clipBehavior,
child: current,
);
}
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
if (foregroundDecoration != null) {
current = DecoratedBox(
decoration: foregroundDecoration!,
position: DecorationPosition.foreground,
child: current,
);
}
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
if (margin != null)
current = Padding(padding: margin!, child: current);
if (transform != null)
current = Transform(transform: transform!, alignment: transformAlignment, child: current);
return current!;
}
As you can see, it is defined in terms of other widgets. And these widgets may also be defined in terms of other widgets and so on. But at some point you will reach the widgets that create the RenderObject
s.
Regarding the reason why the Container
is not 20x20, it's because, as the article explains, sizes are set by parents. So the Container
s size is set by the Container
's parent, which in this case is the screen. And the screen always forces its child to occupy all the available space, in this case ignoring the Container
's desire to be 20x20. The fix here is giving the Container
another parent. One which allows the Container
to choose its own size. For example, both Center
and Align
will let that happen, and that's why you can fix the problem by doing:
void main() {
runApp(
Center(
child: Container(
color: Colors.red,
width: 20,
height: 20,
),),);
}
As to why the screen forces its child to occupy all the available space: That's just the way Flutter creators decided it should be. If you dig into Flutter's code you will find it there. But it's probably best that you just remember this fact.
Hope it helps!
Haidar
Self-taught This site is toxic but - unfortunately - necessary
Updated on January 01, 2023Comments
-
Haidar over 1 year
TL;DR
How can I know the layout rules of a widget (what size will it request from its parent and what constraints will it pass to its children) if there is no documentation about it?
The problem details
I have this very basic app
void main() { runApp( Container( color: Colors.red, width: 20, height: 20, ), ); }
I was expecting the
Container
to have width and height of 20 but I got aContainer
that filled up the whole screen.Reading this article on flutter.dev about understanding constraints, in its last part called "Learning the layout rules for specific widgets ", they mention how to do this by finding the
createRenderObject
method and then finding theperformLayout
method.However this
createRenderObject
method is only available for subclasses ofRenderObjectWidget
. For example, navigating through code ofTransform
widget, I findcreateRenderObject
that returns aRenderTransform
, that extendsRenderProxyBox
, which finally implementsperformLayout
as :@override void performLayout() { if (child != null) { child!.layout(constraints, parentUsesSize: true); size = child!.size; } else { size = computeSizeForNoChild(constraints); } }
I can conclude that
Transform
widget will finally take the size of its child due to this linesize = child!.size;
.But in case of
Container
above, is directly extendsStatelessWidget
. I couldn't find by navigating through its code the methodsperformLayout
andcreateRenderObject
, I could only findcreateElement
, but I am looking for theRenderObject
in the render tree associated with the Container and not the element.The Question
So the question is how to find this render object associated with a stateless widget/stateful widget in order to know the layout rules that this widget will give to its children and will follow them itself in this case?