How build() method of Flutter widget proves “Everything’s a Widget”
We might have heard “Everything’s a Widget” in Flutter. But, do you understand how build()
method of widget work? Let’s dig deep into that method to prove “Everything’s a Widget”.
Note: for non-paywall link of this article, let use: https://medium.com/fluttervn/how-build-method-of-flutter-widget-proves-everythings-a-widget-a49b5efc5cbb?source=friends_link&sk=f1c33cb455bf98104ff44585ef61553f
First, a typical widget such as Container would have constructor like this:
Container({
Key key,
this.alignment, // AlignmentGeometry
this.padding, // EdgeInsetsGeometry
Color color,
Decoration decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
})
At the beginning I thought Container is a complex widget with these properties: alignment
, padding
, decoration
, …
However in Flutter, we also have similar widgets, such as Align, Padding, DecoratedBox,… Do they have any relationship? To understand, we must look at build()
method of Container:
@override
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 (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, child: current);
return current;
}
Let’s analyze from top to bottom (assume that Container has full parameters):
- We begin from the real child of widget, in this case is
child
(assume it’s Text). So, the current structure of Container tree is:Text
. - Next, if has
align
param, then wrap existing child into an Align widget → tree is:Align → Text
. Otherwise if no align param, don’t wrap existing child into Align to avoid complexity/deep level of tree. Now, current child is the Align. - Next, if has
padding
param (of Container or from Decoration), then wrap current child into a Padding widget → tree is:Padding → Align → Text
. Now, current child is the Padding. - Next, if has
decoration
param, then wrap current child into a DecoratedBox widget → tree is:DecoratedBox → Padding → Align → Text
. Now, current child is the DecoratedBox. - Same for
foregroundDecoration
param → tree is:DecoratedBox → DecoratedBox → Padding → Align → Text
. Now, current child is the DecoratedBox. - Next, if has constraints param, then wrap current child into a ConstrainedBox widget → tree is:
ConstrainedBox → DecoratedBox → DecoratedBox → Padding → Align → Text
. Now, current child is the ConstrainedBox. - We have
margin
param, but there is no Margin widget, it’s Padding instead, however is placed near the root of Container → tree is:Padding → ConstrainedBox → DecoratedBox → DecoratedBox → Padding → Align → Text
. Now, current child is the Padding. - Finally, with
transform
param, will wrap into Transform → tree is:Transform → Padding → ConstrainedBox → DecoratedBox → DecoratedBox → Padding → Align → Text
.
Source code of build()
method helps us to have a deeper understanding how properties of widget should be mapped into nested widgets. Yeah, and “Everything’s a Widget” is really true.