Notes: 08. Debug Common Layout Issues
Prerequesites: A decent understanding of Flutter layout, especially Rows and Columns widgets.
One of the most common error that occurs while building layouts is the overflow error. This happens when a widget tries to take more space beyond the boundaries of its parent widget. You can see the overflow warning sign at different parts of our UI. The sign also shows you how many pixels the widget overflows and this can help you make the neccessary adjustments.
For the first one, we can see that the text content is more than the height of the parent widget. We can limit the text to only two lines or we could increase the height of the parent widget if we prefer the full text content to be shown.
We can combine both approaches. But let’s just increase the height of the parent widget like so:
class CardItem extends StatelessWidget {
const CardItem({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 120, // From 100 to 120
And now the overflow warning is gone. Note: if you open up the debug console, this error message would suggest you wrap the card widget with something like an Expanded widget. Doing that would give you an undesired effect. Its better to control the max line of the text and the dimensions of the box because this type of box can hold dynamic data.
Next, we have these chips over here. They are contained in a Row widget. In here, the overflow error occurs because the children are trying to exceed their parent’s bounding box. In this case, the Row has the same width as the screen but the chips are trying to exceed that width which in turn triggers the overflow error on the horizontal axis.
The Row widget doesn’t wrap items to the next line so we have to replace it with the Wrap widget. Let’t do that now:
Wrap(
spacing: 8,
children:[
...
)
The error is gone and the overflowing chips are pushed to the next line.
We could also use flex widgets like the Row to display items side by side with equal widths.
Update your code to the following:
Row(
children: <Widget>[
const Trivia(message: "..."),
const Trivia(message: "..."),
],
)
When you save your work, you should see the overflow warning sign and a notification that says “RenderFlex overflowed by some pixels.” It also gives us some hints on how to solve the error. Reading this could help you but lets use devtools to solve this.
Go ahead and and click on the “Inspect Widget button”. This opens up the WIdgets Inspector pane which is part of devtools. In the previous episode, we opened devtools in a browser but the Flutter plugins for VSCode and Android Studio gives us this feature without us having to leave our IDE.
Now the inpect widget action automatically selects the Row for inspection. And you can see that in the “Layout Explorer” tab. The layout explorer works for only flex widgets just like Row and Column.
The layout explorer is a very helpful tool that gives you a preview while debugging flex layouts. With this tool, we can try different settings to see which one works. You can see the overflow warning is also displayed.
Now, notice the flex dropdown in the flex items. Let’s set the flex to 1 for both widgets. You can see that the overflow error is gone for the horizontal axis and the widgets in the row are displayed correctly.
By giving a flex factor, the widgets fills up the remaining space no matter how small or big the space is. So setting the same flex for both widgets makes them fill up equal space on the horizontal axis. This is the equivalent effect of wrapping each trivia widget with an Expanded widget.
We can also set other properties like the crossAxisAlignment and the mainAxisAlignment here too.
Note: these settings do not update our code. The changes are lost when we do a hot restart. But we now know what to update in our codebase.
Let’s head back to VSCode. Update your code to the following:
Row(
//crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Expanded(
child: Trivia(message: '...'),
),
const Expanded(
child: Trivia(message: '...'),
),
],
)
Save your work and your UI should update accordingly. With this setup, both widgets now have a flex value of 1. Sometimes, you might want to have a header and then a scrolling list below it like many app do.
Using only a Column would cause an overflow error if the items of the Column are longer than the vertical viewport. Instead, we would use a Column and then add the header and ListView inside it. Replace the children of the Column with the following code:
// Main Column ie Body of the Scaffold
Column(
children: [
// New Code
const Text('Some Header Here'),
ListView(
children: List.generate(
20,
(index) => ListTile(
title: Text('Item $index'),
),
),
),
]
)
An exception occurs and it states that: “Vertical viewport was given unbounded height.” This happens because the ListView tries to fill up the vertical viewport as much as possible and this would be till infinity. This would mean that no other widget can be laid out in the vertical space of that Column.
To solve this, we need to limit the height of the ListView to the bounds of the Column. The first thing that might come to mind could be to set the shrinkWrap property of the ListView to true like so:
shrinkWrap: true,
And this solves it to an extent because we can now see the ListView. Setting the shrinkWrap to true forces the main axis of the ListView to be only big as its contents. But in this case, the contents of the ListView is bigger than the Column‘s vetical bounds and that’s why we have the overflow error. Also, the ListView is not scrollable.
To solve this once and for all, remove the shrinkwrap and wrap ListView with an Expanded widget like so:
...
Expanded(
child: ListView()
)
The Expanded widget tells Flutter that the extent of the ListView in the scroll direction should be as big as the available space. In other words, it means that the ListView should fill up the remaining space of the Column.