Demystify Common Flutter UI Error

Understand Constraints in flutter.

Pawan Acharya
codingmountain
4 min readFeb 8, 2024

--

In Flutter Journey we spend the majority of time building beautiful UI. Flutter‘s declarative UI and the developer experience with the predefined widgets and IDE support etc make the journey smoother.

During development, we face errors like UI Overflow, errors on something related to constraints, and many more. Most of the time we solve those even without reading what it means. Because of the ease of hot-reload, within a minute, we can try many approaches, wrap it with other widgets solve it, and move on, all these happen on a subconscious level.

Today I am back with 3 objectives:

  1. Read error
  2. Find the root cause of why it occurred in the first place
  3. Why the solution we are using works

Guess what the code results?

Column(
children: [
Text("Hello"),
ListView.builder(
padding: EdgeInsets.all(8),
itemCount: 5,
itemBuilder: (context, index) {
return Text("data");
},
)
],
)

It gives an error:
Vertical viewport was given unbounded height.

Before going through the solution and how it works, let's understand a layout concept in Flutter

Constraints

Constraints go down.
Sizes go up.
The Parent sets the position.

Constraints Go Down:
The parent widget sets constraints (minimum/maximum width and height) on children. These constraints flow down the widget tree.

Sizes Go Up:
Each child then decides its size within these constraints and informs its parent. The child's size cannot exceed the constraints provided by the parent.

The Parent Sets Position:
The parent positions its children according to their sizes and the layout algorithm of the parent.

We will go through this concept in a while before let’s understand how column works

Column

  • Column tries to layout its children to cover all the vertical space by default, based on its parent constraint.
  • First, it lays out its non-flex children like Text in this case because they cover definite sizes and mention parents about their size.
  • Then it will divide the remaining space among all the children with a non-zero flex factor. If there are multiple then the remaining space is divided based on the flex value.

That is a top-level understanding of how column works.

ListView

Take list view or any other scrolling widget, it tries to take all the possible space in a scrolling direction, for now, it's vertical. This is obvious behavior because it wants to scroll and to scroll it needs as much space as possible, we as a developer should limit the area that it gets to scroll if we want to adjust another widget around it.

Understand how constraint work

/// Based on parent it gives constraint to child
Column(
children: [
/// Text returns its size to parent, so far all good
Text("Hello"),

/// ListView being scrolling widget, wants to take all vertical height
/// But as we learnt how column works, column also have a constraint
/// from its parent so it can't assign a child wanting infinite height
///
/// This is the reason we saw the error
ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return Text("data");
},
)
],
)

Solution?

Now we are aware of why the error occurred in the first place so it should be easy to guess the solution.

Yes, the solution is to confine the height of ListView, there can be many ways like wrapping it in SizedBox or Container with a definite height, but if we want ListView can take up all other remaining space, we can wrap it with the expanded widget.

/// Based on parent it gives constraint to child
Column(
children: [
/// Text returns its size to parent, so far all good
Text("Hello"),

/// There is only one non-zero flex child of column
/// So the column will assign the remaining space to Expanded
///
/// Expanded widget now provides the vertical constraint to listview
Expanded(
/// The ListView widget benefits from the constraints provided by its parent.
/// This ensures that all widgets involved are allocated appropriate space,
///
/// Now everyone is happy :)
child: ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return Text("data");
},
),
)
],
)

So the expanded does a good job for us. Most of the time we wrap with Expanded for many such issues and do hot reload with the hope that it will work.

Now we know why it works.

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

This is another common issue we face, Stay tuned for the in-depth article on this error.

--

--