Whether you are Android developer or Flutter developer, when you start to use the vast number of Flutter widgets, you may guess the size and position of the Widget on the screen, but in reality, you will experience many errors and failures. Flutter widgets don’t always layout the way you expect them to.

It is difficult to predict the size of a Widget without understanding how its constraints are applied. A lot of times, you have no idea why a Widget is bigger than you expected or smaller than you thought. So in this article, let’s try to understand how constraints work and the impact on Widget sizes.

So what exactly are the constraints in Flutter?

The constraints in a Flutter are simple restrictions on the width and height of a Widget

These constraints are specified through the BoxConstraints object. In BoxConstraints objects, the size limits are set to the minWidth, maxWidth, minHeight, and maxHeight properties.

The four width and height attributes can have any value from 0 to double-.infinity. The double. Infinity value means that widgets can have unlimited sizes.

You might come across the terms bounded and unbounded constraints. Bounded means finite constraints, i.e., some specific dimensions, while unbounded constraints mean infinite dimensions, i.e., infinity.

To set the constraints you want, you can use the BoxConstraints constructor.

BoxConstraints({double minWidth: 0.0, double maxWidth: double. Infinity, double minHeight: 0.0, double maxHeight: 0.0, double maxHeight: double.infinity} )Copy the code

Now, before we start talking about how constraints in Flutter work, let’s clear up some important terms about constraints.

Tight constraints, Loose constraints, and Unbounded constraints

Suppose we have a Widget whose BoxConstraints maxWidth and maxHeight are equal to the screen width and screen height, respectively. Now, if we want to force the Widget to fill the entire screen width and height, we must set the Widget’s BoxConstraints minWidth to equal the screen width and minHeight to equal the screen height. So in this case, when we force a Widget to fill a specific size by keeping its minWidth and maxWidth equal to the target fill width, and its minHeight and maxHeight equal to the target fill height, We say we have Tight constraints on the Widget.

On the other hand, if we let the Widget have any width or height from its minWidth to maxWidth and minHeight to maxHeight, then we say we have Loose constraints on the Widget.

So now this means that in order to apply Tight constraints, you have to set the minWidth of BoxConstraints to maxWidth and minHeight to maxHeight. You can also apply Tight constraints to width or height separately. We call it applying tightWidth or tightHeight. For the rest of the article, the term Tight constraint will refer to Tight width, Tight height, or both.

If we set maxWidth, maxHeight, or both to double. Infinity, then we say we have Unbounded constraints on a widget. If you set minwidths to double. Infinity or 0, respectively, then an Unbounded constraint can also be a Tight bundle or Loose constraint.

We can use the BoxConstraints constructor to set Tight, Loose, and Unbounded constraints.

BoxConstraints.tight( Size size )

This will set minWidth, maxWidth to size.width, minHeight, maxHeight to size.height. Therefore, any Widget that applies these constraints will now be forced to fill in the exact size of sie.width and sie.height.

BoxConstraints.tightFor( {double width, double height} )

You can use this constructor and pass width or height to set Tight width or Tight height separately, or pass width and height at the same time to set Tight constraints for both. The constructor has a variant, called BoxConstraints. TightForFinite (). Tight constraints are set only if you are not passing an infinite width or height.

BoxConstraints.loose( Size size )

This constructor sets the Loose constraint, with a minimum width and height of 0 and a maximum width and height provided by the size object, that is, a Widget can choose any size from 0.0 to sie.width and 0 to sie.height.

BoxConstraints.expand()

Put a Tight constraint on the width or height passed to it and an Unbounded constraint on the width or height parameters not passed to the constructor, i.e., double-.infinity.

To set Unbounded constraints, you can also use the default BoxConstraints constructor and set maxWidth or maxHeight or both to double-.infinity.

How constraints work

Let’s take a simple example, a MaterialApp with a Container, as shown below.

The runApp() method sets the MyAppWidget to the Root Widget. When the framework renders MyApp, it is constrained during the layout process, forcing it to fill the entire screen. In other words, MyApp is Tight constrained with dimensions equal to the width and height of the screen. MyApp then sets the constraint on its child, the MaterialAppWidget, which in turn sets the constraint on its child, ContainerWidget.

Here, the Container receives a Tight constraint on the screen size from its parent, MaterialApp. So even if the Container is declared to have a specific width and height of 100 pixels, it is forced to fill the entire screen.

The example code above runs on a device that is 392.7 pixels wide and 737.5 pixels high. (Note: these are logical pixels). The highlighted section in the figure below shows the strict constraints received by the ContainerWidget, the BoxConstraints(w=392.7, H =737.5) and the final dimensions of the Container are 392.7 wide and 737.5, It also ignores its additional constraints w=100.0, h=100.0. You can view these contents in the Flutter Visual Inspector -> Widgets.

Now let’s package the Container in a Scaffold as shown in the following code. When we run this code, we get a Container of size W =100.0, h=100.0.

So why has the Container changed its size now?

This is because that Scaffold has a Loose constraint on the Container, even though that Scaffold itself has a Tight constraint from its parent. Since the Container has Loose constraints, it is free to choose any size between the minimum and maximum constraints, in this case its size is 0 to screen size. But the Container itself has additional constraints of 100 width and 100 height. So the Container chooses 100×100 because it is under the Loose constraint.

What happens when constraints are passed from parent to child?

As the example above shows, a parent Widget cannot simply pass the constraints it receives to its children. Conversely, the parent Widget can change the constraints of its child Widget from Tight to Loose, and vice versa. If the parent Widget has a Tight constraint, the child Widget has no choice but to fill the exact size set in its constraint. On the other hand, if the parent sets loose constraints, the child Widget is free to choose its own size up to the maximum width or height. Under the Loose constraint, some widgets take up the maximum size allowed, while others only take up the minimum size it needs.

This makes Widget constraint behavior a little more complicated, because now we need to understand how each Widget behaves under different conditions.

There are three possible size types that a Widget can eventually have

In general, the final Widget size may end up in one of three sizes.

  • Under the Loose constraint, it can become as large as possible.
  • Under the Loose constraint, it may become as small as possible.
  • Under Tight constraints, it can become a specific size.

So how do you predict the final Widget size on the screen?

Well, first of all, you should know which of the three options a particular Widget will choose under different conditions, such as Tight constraints, Loose constraints, Unbounded constraints, it has one child, it doesn’t have more children, or it has multiple children.

We must understand the specific behavior of each layout Widget. So it’s a good idea to study the common layout components of Flutter to understand how each Widget behaves under different conditions.

Here are some questions to help you predict the size of your Widget.

  • Does the parent Widget have a Tight or Loose constraint on its children?
  • Whether the child widgets have their own additional constraints. If so, what are the synthetic constraints resulting from parent and child constraints?
  • Does the child Widget override the constraints of the parent Widget?
  • If the combined constraints from the parent and the child cause the child Widget to have a Loose constraint, then we should check the specific behavior of the child Widget to see if it chooses to become as large or as small as possible.
  • Are there Unbounded constraints from the parent Widget, and do the child widgets have Unbounded constraints in the same direction?

Because layout components have their own specific behavior, it is important to pay attention not only to the general rules but also to the specific constraint rules for layout components in order to correctly predict the final size of a Widget.

One of the most commonly used layout widgets is Container. Container acts as a parent Widget and passes the same Loose or Tight constraint to its child widgets.

Here is the final size of the Container under different conditions:

Example: Container has unlimited parent constraints, no children, no alignment.

Container tries to be as small as possible for its given height and width.

Case: there are parent constraints, self-constraints, such as specific height, width, but no children, no alignment.

Container tries to determine the smallest possible size based on the combined constraints created by its parent constraints and its own constraints.

Case: Parent constraints with boundaries, no self-constraints, no children, no alignment.

The Container extends to fit the constraints provided by the parent, that is, the Container tries to be as large as possible.

Case: There are unbounded parent constraints, there are no self-constraints, there are children, there are alignments.

Container tries to wrap its size around the child.

Case: Parent constraints with boundaries, no self-constraints, children, alignment.

The Container tries to expand to fit the parent body, and then arranges the children within itself.

Case: Parent constraint, no self constraint, child constraint

The Container passes constraints from the parent to the child and matches its size to the child.

As you can see, it is not easy to predict the final size of a Widget without knowing the specific behavior of the most commonly used layout widgets.

How do I override the parent constraint and control the size of the child Widget

Flutter provides us with some useful widgets to override constraints passed from parent to child.

Example: Remove all constraints that the parent Widget sets on its child widgets

Wrap child widgets with UnconstrainedBox.

Example: Set a new size constraint for the child Widget within the parent constraint boundary

Wrap child widgets with a SizedBox. We can also use variations of SizedBox, such as FractionallySizedBox to set the size of the child Widget to a portion of the total available space, and SizedOverflowBox to set a specific size and allow the child Widget to overflow.

Example: Add additional constraints along with the constraints set by the parent Widget

Wrap the child Widget with ConstrainedBox

Example: Limit the size of a child Widget within a scrolling parent Widget, and have unlimited restrictions on its scrolling direction

Wrap child widgets with limitedBoxes

Example: Overwrite parent constraints with new constraints and even allow children to overflow parent without black and yellow stripe warnings

Wrap child widgets in an OverflowBox

Example: Scaling child widgets

Wrap child widgets in a FittedBox

Example: Control the size of child widgets within a row or column Widget

Wrap each child Widget in a Flexible or Expanded

Common constraint problems and solutions

Error: BoxConstraints forces an infinite width.

If there are any Unbounded constraints from the parent, and the children also have Unbounded constraints in the same direction, i.e. in the width or height direction, it will result in the above error. This error is for width. This is because Flutter cannot render infinite size. Either parent or child must set a boundary so that the frame knows the size it needs to render.

Scroll widgets like ListView have Unbounded constraints on their scroll direction. Therefore, if you give it a child that also has Unbounded constraints on the scrolling direction, the same error will occur. To resolve this error, use LimitedBox to wrap child widgets.

Error: RenderFlex children have non-zero flex but incoming height constraints are unbounded

This error can occur in Flex widgets like column, for example, if the parent Widget of the column has an Unbounded constraint on it, and the height of one of the widgets in the column is set to double. Infinity, which is an Unbounded height constraint. Flutter will fail because it cannot determine the exact size of the child widgets. You can solve this problem by wrapping each child Widget with Flexible or Expanded.

Black and yellow stripes shown on screen overflow

In general, text sizes or image sizes overflow when they do not fit within the parent constraint. In this case, you can use a FittedBox to solve the problem.

Columns or rows can also overflow when their children do not fit their spindles. You can solve this problem by wrapping each child Widget with Flexible or Expanded. Or change column or row to a Listview.

conclusion

In general, there are three types of constraints. Tight, Loose, and Unbounded.

The screen passes Tight constraints to the root Widget to make it the same size as the device screen. Thereafter, each parent Widget passes constraints to its children.

Layout widgets have their own specific behavior:

  • When passing a constraint to a child, the parent can change the Tight constraint to Loose, or pass it unchanged.
  • The size of the Widget may vary under different conditions. This depends on various factors such as its child size, its parent size, its own constraints, parent constraints, and so on.
  • In general, a Widget can be as large as possible, or as small as possible, or a specific size.
  • Use the BoxConstraints constructor to set the constraints.
  • We can also override parent constraints with Box widgets such as UnconstrainedBox, SizedBox, ConstrainedBox, and so on.
  • Unconstrained constraints in parent and child constraints can cause rendering errors. Flutter cannot render to an infinite size.

This article is translated from medium.com/naresh.idi…

I would like to recommend my website xuyisheng. Top/focusing on Android-Kotlin-flutter welcome you to visit