Flutter Deep Dive, Part 3: “RenderFlex children have non-zero flex…”

Deep Dive Part 3, by Scott Stoll. Twitter: @scottstoll2017

This article is part of a four-part series. You can find the others here:

So, are you ready to get back into it? Yeah, me neither… but here we go, anyway.

The Flex

A Flex is flexible in that it can adjust its size based on its children. But don’t confuse the fact that it can be flexible with an actual Flexible because that’s a different Widget and you’ll have problems if you get them confused.

So don’t confuse the Flex and the Flexible, or the fact that a Flexible has a flex(which is not the same thing as a Flex), and we want to remember that how the flex gets applied depends on the FlexFit.

Hot Tip: A Flex can have any number of Flexibles but a Flexible can only have one flex. And remember, a Flex is not a flex. Got that?

Okay, I’ll be nice. No more jokes for a little while.

The Flex is the Widget that Row and Column extend. They’re the only two Widgets in Flutter that extend Flex (as of Flutter 1.0). You could just use a Flex directly instead of Row/Column and set the AxisDirection property to up, down, left or right; but that would probably confuse people so maybe you should just stick to using Row/Column.

You want to make sure to remember that when there is something wrong with your Row or Column, the error isn’t going to say anything about a Row or Column. In fact, it won’t even say anything about a Flex.


Enter the RenderFlex

Remember we talked about the three layers of a Flutter cake? The Widget, Element and Render layers? You need to understand the basic concept of those layers in order to understand why the error says “RenderFlex children…”. It’s because the error isn’t occurring in the Widget layer, it’s occurring in the Render layer, and the Render layer doesn’t have Rows or Columns. It has theRenderFlex.

Many Widget class names have counterparts in the Render layer, with “Render” in front of them.

What happens is when you use a Row or Column in the Widget layer, it gets rendered as a RenderFlex in the Render layer.

Remember, a Row or Column is just a Flex with certain values already hard coded. So, the Render layer just uses a RenderFlex, then sets the parameters to whatever is needed to make the Row or Column you want. As we said before, you could just use a Flex in the Widget layer instead of a Row or Column, but that would confuse people.

But the Render layer does use a RenderFlex, and that’s why the error says RenderFlex and not RenderRow or RenderColumn.


Flexible and FlexFit

Flexible and FlexFit are a matched set. There’s a subclass of Flexible you’re probably already familiar with, the Expanded. We’ll dig into that in a moment but for now, let’s take a look at Flexibles and their fits.

The idea of a Flexible is it gives you a way to resize a Widget. It can make its child larger or smaller and it can be a great way to resize an image, among other things.

Quick Image Resizing Tip:

If the child of a Flexible is too large to fit inside the Flexible’s parent, then the child will be resized so it does fit. This is important because it’s an easy way to deal with an Image that’s too large and causing an overflow error (the black and yellow bars). Just stuff the image into an Expanded and put that Expanded into a Row or Column. Voila! It will be automatically resized to fit in whatever space is available, at runtime, and without you needing to write a bunch of code to do it.


The fit… My Wife Says It’s All About the Shoes

This might sound like a joke, but it isn’t. She really does say it’s all about the shoes.

But back to business…

The easiest way to quickly understand fit is if you imagine a balloon inside of a shoe box. The Flexible is the balloon and its parent Flex (Column/Row) is the box.

If you blow the balloon up as much as you can while it’s inside of the shoe box, how does the balloon fit in the shoe box? Tight or loose?

Now you understand fit. Yes, it’s really that simple.

FlexFit.tight isn’t just going to gently get bigger until it gently touches the edges. It’s going to push to see if it can get its parent to give it more room… and it will push hard (along the main axis) until the parent (Row/Column) starts enforcing some boundaries and says, “No more. That’s all you get.”

Here’s the funny thing. Our Flexible (usually an Expanded), is going to have a child. So, you might be wondering how the child’s size gets calculated into all of this.

Answer: It doesn’t.

You can set the height or width parameter of the child to 1.0 or 100000.0; if the size parameter is along the main axis then it doesn’t matter because it’s going to be ignored. This is because a Flexible with FlexFit.tight (AKA an Expanded) is only concerned about what limitations its parent is placing on its size, not what its child wants.

That takes care of FlexFit.tight, but what about FlexFit.loose?


The Trouble with Expandeds…

Take a look at this constructor:

Look at the third parameter of the call to super(). The fit has been hardcoded to FlexFit.tight, and you can’t change it. When you start getting RenderFlex errors involving an Expanded, that’s usually half the problem. This is what the error is talking about when it says “RenderFlex children have non-zero flex”.

The next thing to understand is how the layout algorithm handles multiple children within a single parent. This is how laying out the children of a Flex is handled.

Next up, Part 4: The Dreaded Flex Layout Algorithm…

You can stalk the author on Twitter at https://twitter.com/scottstoll2017