How build() method of Flutter widget proves “Everything’s a Widget”

Bui Minh Triet
FlutterVN
Published in
3 min readAug 13, 2019

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.

--

--