Understanding Flutter Constraints Rule 2 : Sizes Go Up
A parent can only pass box constraints, not sizes. The size that a widget should take within the given constraints is decided by the widget itself. This article focus on explaining that rule.
In the previous article, we learned about Flutter constraints rule 1: Constraints go down. This article focuses on the second rule: Sizes go up. So Let’s get started.
What Does “Sizes Go Up” Mean?
The widget constraints are passed from top to bottom in the tree. However, the size that a widget should take within the given constraints is decided by the widget itself.
From our previous example, a parent can only pass box constraints to its child, not sizes.
The parent cannot tell it’s child to be of size 50 × 50 unless it is enforced by using tight constraints. (Check Rule 1)
However, it can inform its child as follows:
Hey, child. I am giving you a box of 100 × 150 with loose constraints, allowing you to choose any size you want within this box.
Now, the child widget decides to be of size 50 × 50 and communicates this information back to its parent.
It is the child widget itself that determines its own size within the given constraints set by its parents.
In the above example, the ConstrainedBox (parent) can only inform the Container (child) that it can occupy any size within a 200 x 150 box (constraint). It is now up to the Container to decide whether it will use the entire available size or only a smaller portion of it.
Now, based on the size information provided by the child, the parent aligns the position of the child on the screen. (Setting position will be covered in the next article)
So, from the widget tree perspective, the size is passed from bottom to top; hence, the rule is "Sizes go up".
Since it's a widget tree, the same rule applies to the child when it becomes a parent.
Now, the important question is why the Container occupies the full available size.
How Does the Widget Choose Its Size?
Well, the answer is “It’s complicated”.
Although the widget follows the constraint system rules, how it defines its size completely depends on its own implementation and how the author has designed it.
Let's take an Icon widget. In the example below, we are passing a tight constraint to force the Icon size to be 150.
But we can see from the output that the Icon is not taking the size of 150.
Why?
It's because the Icon widget is designed based on Material theming. It takes its size of 24 from the IconThemeData object in the MaterialApp theme.
We figured this out by reading the documentation and source code of the Icon widget.
So to change the Icon size, we simply pass the size value in the Icon widget or change the icon size in the material theme.
Let’s fix this with the theme-based approach:
Now it's working as expected.
When it comes to its own size, each widget is designed to solve a specific problem and has its own specific behavior. To correctly predict a widget's final size, we should be aware of not only the general rules but also the layout widget-specific constraint rules.
For example:
Container widget will, by default, take the maximum height/width of constraints passed from the parent.
DecorationBox widget will take the minimum height/width of constraints passed from the parent.
Text widget will style its text based on the DefaultTextStyle from the Material theme widget. For more information on text styling, check the video below.
For more examples, you can check out this Codepen and GitHub repo by Marcelo Glasberg.
Common Widget Size Problems and Solutions
Black and yellow stripes are shown on the screen overflow.
This is one of the common errors developers face while building UI.
And most of the time, we encounter it while building the Row/Column widgets. This happens when the children try to be larger in size than the given constraints by the parents.
We can fix this issue in the Row/Column widget by wrapping each child widget with the Flexible or Expanded widget. Alternatively, we can change the Row/Column widget to a ListView.
Conclusion
Generally, the final widget size may end up being one of the following three sizes:
It may become as big as possible under loose constraints.
It may become as small as possible under loose constraints.
It may become a particular size under tight constraints.
This makes the widget constraints behavior somewhat complicated, as now we need to understand each widget's specific behavior under different conditions.
The best way to understand how a widget decides its size is to go through the documentation or the source code.
Due to this rule, we have some limitations on widgets:
Since the parent's size and position, in turn, also depend on its own parent, it's impossible to precisely define the size and position of any widget without considering the tree as a whole.
We cannot obtain the size of the child in the build method as we do with constraints. Here is the article explaining how to get the size and position of a widget.
That’s it for the second rule folks!! If you have any questions please let me know in the comments and subscribe to stay tuned for the remaining part of the series.
Resources and Credits
From the official Flutter, doc check out Understanding constraints.