Intrinsic widgets | Decoding Flutter

In Mobile App Development

Intrinsic widgets | Decoding Flutter - read the full article about Flutter, Mobile App Development and Native and cross-platform solutions from Flutter on Qualified.One
Youtube Blogger

♪ [intro music] ♪ If youve watched a handful of Flutter videos on YouTube, one phrase youve likely encountered is the age-old rule for laying out widgets that "Constraints go down.

Sizes go up.

Parent sets position." What this means is that widgets in Flutter tell their children how big they could be, the children decide how big they will be, and the wrapping widget places them.

Admittedly, its not a great parenting strategy, but it sure does make for efficient layout algorithms for your apps.

But sometimes, a widget doesnt know how big it wants to be on its own.

As anyone whos ever had siblings will tell you, sometimes we want to be as big as our brother or sister.

So if thats a fair desire for human children, can widget children do the same thing? In the normal course of things, they cannot, but where theres a will theres a way, and Flutter has a way.

The widget that helps you do this is called IntrinsicHeight or IntrinsicWidth, and their difference is... well, kind of obvious.

But what problem does it solve again? Widgets that need to know how big their siblings will be before they can commit to their own size? When does that happen? Imagine you have a classic table layout with rows and columns, and you want to center your header text in the first row.

Centering things is easy in Flutter, but only once you know how big that things parent is.

But this is a table, where the height of rows depends on the tallest cell anywhere in that row, and the width of columns depends on the widest cell anywhere in that column.

Uh-oh! Now we have widgets that wont tell us how big they should be, until all of their siblings have answered.

This is like one of those situations in high school, where two kids wont get off the phone because they keep telling each other that they love the other one more.

"Im going to be as tall as you are!" "No, Im going to be as tall as you are!" How does Flutter use intrinsic height and width to end this madness and get our star-crossed widgets to hang up the phone? Well, first, and ask all of them, "Seriously, how big would you be if you had no siblings?" And then it gets plain answers.

The intrinsic widget then scans those answers for the biggest value, and voila... thats how big all the children will be.

Okay, so now that weve covered the situation, were ready to get started.

What are some real-world scenarios where you might use an intrinsic widget? We already mentioned spreadsheet-style tables earlier, but for those, you probably just use a Table widget and be done with it.

Note that table widgets achieve this functionality with intrinsics.

Imagine you have a custom ListTile containing three widgets, and you know the innermost one will be the tallest.

By default, Flutter will render your layout like this, with all elements vertically centered within the row.

And what if you dont want them centered? What if you want your smaller widgets aligned to the top or bottom? To achieve this effect, actually, you still shouldnt use intrinsics.

Rows can do this for you with their CrossAxisAlignment property.

Set CrossAxisAlignment to start, and the yellow rectangles will move to the top.

Okay, so we still havent found a scenario that needs intrinsics, but were close.

All our designers would need to want is this: where the widgets dont share a single alignment but instead, each have their own.

This new layout is above CrossAxisAlignments pay grade.

"Well, thats okay," you might think.

Youll just wrap each relevant widget in an Align widget and specify topCenter and bottomCenter as needed.

You do that, hot reload and...

nothing changes.

Neither yellow box has moved.

Whats going on here? In certain situations, like rows or columns that appear within a scrollable area, the layout algorithm is such that widgets which normally grow to the full size of their parents suddenly stop doing that.

If you think about it, this makes sense.

Scrollable areas have infinite inner height, so you have to be careful before you allow any of their children to expand to the full size of their parent.

So thats the why.

The what is that the Align widgets you added perfectly wrap the two yellow squares.

Congratulations! Youve successfully aligned the left yellow box to the top center of its pink wrapper and the right yellow box to the bottom center of its pink wrapper, which... does nothing! To fix this, we need to figure out how to get those alignment widgets to think of themselves as being as tall as the blue square.

Once we do that, we could use topCenter and bottomCenter again and finally get what we want.

Weve now arrived at those hypothetical sibling widgets that dont want to commit to a certain size until everyone else has already answered.

The solution for this is IntrinsicHeight, which asks the row how tall it wants to be, prompting the row to answer...

Um... I dont know, hold on a second.

"Kids, how big would you each be if you were an only child?" The row gets all those answers, tells the IntrinsicHeight, and then thats how tall everyone is.

Great! Now armed with when to use and not to use intrinsics, were ready to add them to our mental tool belt.

Like any good Flutter developer, to really learn the ins and outs of a new widget, you read its summary in the API Docs, and in doing so, encounter this alarming paragraph: "Intrinsic height or width is relatively expensive because it adds a speculative layout pass before the final layout phase.

Avoid using it where possible.

In the worst case, this widget can result in a layout that is O(N²) in the depth of the tree." Okay, wait, are we supposed to use this widget or not? Whats going on here? The Docs say to avoid it where possible, so its important to be comfortable with the details.

Here are a few rules of thumb for knowing where your intrinsic widgets will fall between dirt cheap and super bougie.

First, the deeper the subtree, the more expensive layout is, and thus, the more expensive laying out widgets twice is.

Second, Flutter caches those sizes after the first speculative layout, so until those exact widget subtrees change, Flutter wont pay that speculative layout cost again.

Third, Flutter short circuits as soon as any widgets can provide a definite sizing answer.

Know how big a deep, complicated subtree will ultimately be? Wrap it in a SizedBox and the speculative layout pass will know to save the rest of the work for the real layout phase.

So, what does all this mean? Lets take a little quiz.

Imagine you have a scrolling page with ListTiles, each containing some complicated intrinsics.

How expensive would this be? The answer is "Probably not that bad." You might think that because the ListTiles are moving up and down the screen, the cost will be paid on every scroll event.

But those sizes will be cached and only require one speculative layout pass per ListTile.

Final recommendation? Proceed with intrinsics.

What about that same situation, but now the inside of each ListTile is also animating back and forth between various states? The answer here is "Potentially expensive." Each animation will likely bust the intrinsic sized cache, and force full speculative layouts every frame.

Final recommendation? Proceed with caution and benchmark.

And what about that part about the worst case and exponential speculative passes? Well, thats a subtree consisting entirely of intrinsics, which is very unlikely to be what you actually need.

Final recommendation? Put down the keyboard; nobody has to get hurt.

Intrinsics can be a little confusing and honestly usually arent needed.

But when its their time to shine, theyre your only option short of writing your own RenderObject.

So the next time two of your widgets wont give you a straight answer about how tall they each want to be, just show them this video and get on with your life.

And for everything else about Flutter, head to ♪ [outro music] ♪

Flutter: Intrinsic widgets | Decoding Flutter - Mobile App Development