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

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

- Part 1: “RenderFlex children…”, wading into the Baby Pool
- Part 2: Taking the Plunge
- Part 3: A Flex is not a flex
- Part 4: The Flex Layout Algorithm

Before we start, you might want to take a moment to go to the bathroom and refill your coffee, tea, vodka or whatever else you’re drinking. You might also want to take your headache medicine of choice, now. A gram of prevention is supposed to be worth a metric ton of cure or something like that…

Ready? Let’s go.

There are six steps involved in laying out the children of a `Flex.`

I’ll paste what the docs say about each step and then try my best to translate each one into human:

## Step 1:

“Layout each child a null or zero flex factor (e.g., those that are not

`) with unbounded main axis constraints and the incoming cross axis constraints. If the crossAxisAlignment is CrossAxisAlignment.stretch, instead use tight cross axis constraints that match the incoming max extent in the cross axis.”`

Expanded

First, lay out all the children that don’t have a `flex`

parameter at all **or **a `flex `

parameter that’s set to zero. Believe it or not, we’re not going to set a minimum or maximum size for these children in the direction of the mainAxis *yet*. That’s what *“with unbounded main axis constraints”* means.

If the `CrossAxisAlignment`

parameter is set to stretch, then make the child as big as its parent will allow in the cross axis.

## Step 2:

“Divide the remaining main axis space among the children with non-zero flex factors (e.g., those that are

`Expanded`

) according to their flex factor. For example, a child with a flex factor of 2.0 will receive twice the amount of main axis space as a child with a flex factor of 1.0.”

In human: Divide the leftover space along the main axis among all the children that do have a specified `flex`

** and** that

`flex`

is not zero. However, not all children get treated the same way. Maybe you like some children more than others, and you want to give them more space than the others.So how do you do this? You determine who gets how much space by their `flex.`

If you have 3 children and the `flex`

of each is 1, 4, and 5; then here’s what happens:

- The total amount of all
`flex`

is calculated. Here, 1 + 4 + 5 = 10, so we have 10`flex`

in total. - The first child has a
`flex`

of 1 and the total is 10, so the first child gets 1/10 of the leftover space*reserved for it.*

Stick a pin in that phrase, “*reserved for it.” *We’ll be back for it later.

- The second child gets a
*reservation*for 4/10 (40%) of the total leftover space and the last one, your favorite, gets 5/10 (half)*reserved*for it to use.

*We now calculate the actual number of dp that will be **reserved for each one**, based on what their final share of the leftover space **could be**.*

Not is. *Could be.*

## Step 3:

“Layout each of the remaining children with the same cross axis constraints as in step 1, but instead of using unbounded main axis constraints, use max axis constraints based on the amount of space allocated in step 2. Children with

`Flexible.fit`

properties that are FlexFit.tight are given tight constraints (i.e., forced to fill the allocated space), and children with`Flexible.fit`

properties that are FlexFit.loose are given loose constraints (i.e., not forced to fill the allocated space).”

The first thing a human must realize here is that we didn’t lay anything out in Step 2; we just did a little math because we have to know exactly how many dp (written as a double) each of those children is going to have reserved for it to work with.

Each child that has a flex comes into this process without its main axis constraint set (the parent hasn’t put any limits on its size *yet*, so it’s still “unbounded”).

So the next step is going to be to make each child’s maximum “main axis constraints” be the maximum number we came up with in Step 2. This will allow it to be *up to* that big, *but never bigger than that. **Never forget “never bigger than that”.** It becomes important later on in this article and even more important anytime you’re coding a UI.*

But wait, we’re not done yet. Remember that shoebox thing called `fit`

? It’s half of the problem when all this goes horribly wrong. If the `FlexFit`

is loose, it basically means no one cares. Just be as big as the child says it wants to be, just like it didn’t even have a`flex`

.

*‘Hey, Scott? If no one cares and the *`Flexible`

* is just going to make the child be the same size it was going to be anyway, then why would I want to use a loose fit? Why not just use the child without wrapping it in a *`Flexible`

*?*

Hey reader? That’s a really good question and you know what? The only reason I can think of is that it’s one of two things we need to do if we want to fix our Boogeyman RenderFlex error. Other than dealing with *“RenderFlex children have non-zero flex but incoming height constraints are unbounded”, *no-one I’ve spoken to was able to think of a single reason to use a loose fit, ever.

Back to our example… If the `FlexFit`

is tight, then the child tries to take up all the leftover space it can get. Remember, “tight” means to push up tight against whatever limit your parent is setting. Again, this is what an `Expanded`

does, because it’s really just a `Flexible`

with its `FlexFit`

hard-coded to tight.

## Step 4:

“The cross axis extent of the Flex is the maximum cross axis extent of the children (which will always satisfy the incoming constraints).”

Figuring this one out took three dictionaries and a Gypsy named Wanda with a Tarot deck.

The simple version? *The cross axis size of a **Colum / Row** is going to be as big its biggest child in that direction. *If a child tries to be bigger than that, throw an error. *Usually,* this will be an overflow that shows you the black and yellow bars but if you nested a

inside of another *Colum / Row*

, then you might see one of the brothers or sisters of our Boogeyman error:*Colum / Row*

“BoxConstraints forces an infinite height (or width).”

(in the cross axis)

Thank you, Wanda.

## Step 5:

“The main axis extent of the Flex is determined by the mainAxisSize property. If the mainAxisSize property is MainAxisSize.max, then the main axis extent of the Flex is the max extent of the incoming main axis constraints. If the mainAxisSize property is MainAxisSize.min, then the main axis extent of the Flex is the sum of the main axis extents of the children (subject to the incoming constraints).”

- If the
`MainAxisSize`

is set to max (this is default) the length of the`mainAxis`

of your`Flex`

/`Row/Column`

is going to be as large as its parent will allow it to be. - If the
`MainAxisSize.min`

is being used, then just add up the lengths of all of its children along the`mainAxis`

and then it’s going be that big*subject to the incoming restraints.*

There’s another one you should never forget: **“ subject to the incoming restraints.” **If you forget that one, you will definitely regret it. Don’t ask how I know that… it’s embarrassing.

## Step 6:

“Determine the position for each child according to the mainAxisAlignment and the

`crossAxisAlignment.`

For example, if the`mainAxisAlignment`

is MainAxisAlignment.`spaceBetween`

, any main axis space that has not been allocated to children is divided evenly and placed between the children.”

Have you noticed that we still haven’t put anything into the `Column`

or `Row`

yet? We’ve figured out how big each child is supposed to be in both the main and cross-axis, and we’ve figured out how big the `Column`

or `Row`

* could be. *But we still haven’t put any children into it… and we can’t do that yet because we have no idea which end of the `Row`

or `Column`

we’re supposed to start from.

Ooops.

We’re also going to need to know if we’re supposed to squish the children together, or spread them out and leave some space in between them.

All those things are outside the scope of this article because we’re here to deal with those error messages and the things that cause them. We’re not covering every single thing about `Rows`

and `Columns.`

I’ll let someone else write that deep dive.

In the following code, what is the height of the `Column`

?

So what do you think? 100 +10 = 110 and there’s no flex. The `mainAxisSize `

is set to min, so the `Column`

will not try to fill all of the space its parent gives it. Therefore the `Column`

is going to have a height of 110, right?

Wrong.

## Read the Fine Print

Let’s review:

- Step 1: “
*Layout each child a null or zero flex factor (e.g., those that are not*`Expanded`

*) with unbounded main axis constraints and the incoming cross axis constraints.”*

Both children have a fixed height and neither uses a flex factor, so they get laid out immediately. There are no other children to place, and the total height of both children is 110.0

- Step 5:
*“… If the mainAxisSize property is MainAxisSize.min, then the main axis extent of the Flex**is the sum of the main axis extents of the children …”*

So does that mean the `Column`

*has to be* the size of the sum of its children? No, that’s not what it says. But did you forget “never forget this” part two?

“subject to the incoming constraints.”

It’s like you need to be a lawyer to write an app these days… you gotta read the fine print, people. When you put it all back together, the part of Step 5 that has to do with `MainAxisSize.min`

is: *“If the mainAxisSize property is MainAxisSize.min, then the main axis extent of the Flex is the sum of the main axis extents of the children (subject to the incoming constraints).”*

What’s happening here is the incoming constraints are telling `MainAxisSize.min`

to *sit down and shut up *because they’re going to force our `Column`

to be as big as the max constraints the parent passed in. So here, the fact that mainAxisSize is set to `MainAxisSize.min`

doesn't matter, because constraints were passed in.

But wait, the parent `Container`

of our `Column`

didn’t have a specified size, *so where did the constraints come from?*

## Passing Constraints Down the Tree

The answer, dear reader who now has a headache, lies in the source code for the `Container:`

container.dart line 171:

… the widget has a [child] but no `height`, no `width`, no [constraints], and no [alignment], and

and sizes itself to match the child.the [Container] passes the constraints from the parent to the child

Clearly, this is only part of a much larger (and even more confusing) set of comments, but the part we care about is in bold. Our parent container has *“no `height`, no `width`, no [constraints], and no [alignment]”.* So, in our `Container`

is going to pass on the constraints that were given to it from its parent, and then it will size itself to match its child (our `Column`

).

Let’s break it all down so it’s easier to see:

- Our
`SizedBox`

has a height of 500. - The first child is the first
`Container.`

It has no specified height and its parent`SizedBox`

is passing a constraint of 500 dp high. The`Container`

is going to pass this 500 dp high constraint to our`Column.`

- The second child is our
`Column`

, which needs to consider its children before it can calculate a height. It has its`mainAxisSize`

set to`MainAxisSize.min,`

so it willshrink itself down to match whatever size its children decide to be.*try to**But try to do it is not the same as will do it!* - Children 3 and 4 are
`Containers`

inside of the`Column,`

with heights of 100 and 10, for a total height of 110. - The
`Column`

will say that its children add up to 110 and its`mainAxisSize`

is min so it wants to be 110… - Then the parent of the
`Column`

says, “Nope, you’re subject to the incoming constraints I gave you and that’s how big you’re going to be.*Now sit down, be quiet and do as you’re told.*” - So, the first
`Container`

is going to be 100, the second one is 10 and then*there is 390 dp of dead space inside the**Column.*

When you look at your screen, it looks like the `Column`

is 110 dp and there is 390 dp of dead space below the Column. But that 390 dp of dead space is actually *inside* of the `Column.`

*And that’s how a **Container** with its **mainAxisSize** set to min and 110 dp worth of children **still **ends up being 500 dp even though there’s no flex in any of this.*

# But wait! There’s more!

Now Alice, make sure that safety rope is good and tight around your waist because from here on the rabbit hole *really* starts to get weird…

How tall is the `Column`

this time? What size is the green `Container`

going to be? What size is the yellow `Container`

going to be?

## Thinking it Through…

So, the `Flexible`

with a tight fit is really just an `Expanded,`

so that’s easy. And the `Flexible`

with a loose fit will only take up 10, since a loose fit means it doesn’t have to take all the space given.

The `SizedBox`

is going to pass a constraint of 200 down through the `Container,`

so we know our `Column`

is going to be 200 dp high because we learned that in the last Pop Quiz. And if the yellow `Container`

takes 10, there’s going to be 190 left over. And we all know that an `Expanded`

is what we use to take up any remaining space in a `Column,`

so the green one has to be 190, right?

*Not even close.*

Welcome to Wonderland, Alice. The Laws of Physics don’t apply here.

## Behind the Wizards Curtain

Remember that time long, long ago in a part of this article far, far away? That time I had you stick a pin in the phrase *“reserved for it” during* Step 2?

I also told you to never forget something in Step 3. Did you forget it?

In Step 2 we said:

*“We now calculate the actual number of dp that will be **reserved for each one**, based on what their share of the leftover space **could be**.”*

In Step 3 we said:

“This will allow it to be up to that big,but never bigger than that.Never forget “never bigger than that”. ”

What happened here is this:

- Both
`Flexibles`

have a flex of 1,*so in Step 2 the amount of space that was reserved for each one**was the same**. They**both**got reservations for 50%**each**.*So, 100 dp was set aside for each of them, and now they each*can never be bigger than that*100 dp*.* - The green
`Container`

(on top) is surrounded with a`Flexible`

that has a tight fit, so it’s going to take every last dp it can get (100). - The yellow
`Container`

(on bottom) is surrounded with a`Flexible`

that has a loose fit, so doesn’t care that 100 dp was reserved for it. It’s only going to use 10.

End result:

- The
`Column`

is 200 dp - The green
`Container`

is 100 dp - The yellow
`Container`

is 10 dp - There is 90 dp of dead space
*inside*the`Column`

** I want to be absolutely clear here so that there is no misunderstanding: **There are going to be times when your

`Flex`

has unused, extra space *inside*of it. Even if you put

`Expanded`

into the `Flex,`

that does not guarantee every pixel of available space is going to get used by the children of the `Flex. `

The `Flex`

is usually going to use all of the available space, often because the incoming constraints are forcing it to, but there are going to be times when there is *unused*leftover space

**of your**

*inside*`Flex`

… *especially if you have a loose fit*

*Flexible*

*in there.*

So when people tell you to use an `Expanded`

to eat up all of the leftover space in a `Row / Column,`

you need to remember that if there is a loose fit `Flexible`

in there then you need to throw everything most people* *know about `Rows, Columns`

and `Expandeds`

… wait for it…

Right down the Rabbit Hole.

*And they lived with a horrible migraine forever after.*

THE END

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