# Flutter Layout Constraints This tutorial is designed to help Flutter beginners master the essential concepts of the constraints rule and the layout system, an area where many beginners hit a wall. ![ConstraintLayout](https://hackmd.io/_uploads/HyZGG62lxx.png) # Introduction In Flutter, building layouts is easy, thanks to its rich widget catalog and its flexible composition system. However, Flutter's layout system is indeed quite different from other systems like HTML/CSS layout, and understanding this difference is crucial when transitioning to Flutter. HTML/CSS layout relies on relative units and a flexible sizing system, where an element can freely choose its size and grow/shrink based on content or container size, while Flutter uses constraint-based layout, where every widget's size will be within the constraints given by its parent; it's a top-down sizing rule. This makes Flutter layouts very predictable and avoids issues like content overflow or unexpected resizing that we often face on the web. By understanding constraints, you can take full control over how your UI looks and behaves, no matter the device size. # Part 1: Layout System Basics ## The Core Rule > Flutter layout can't be understood without knowing this rule, so you should learn it early on. ![article-hero-image](https://hackmd.io/_uploads/ryjW2mIggx.png) *Okay. Okay. But wait, what are constraints?* ## Understanding what Constraints are It's pretty simple. Constraints are nothing more than a **set of 4 doubles**: A minimum and maximum height, and a minimum and maximum width! ``` dart BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ) ``` So, it's about the size of a widget and the boundaries of its height and width. *Got it. Continue, please!* ## The Layout Process: Single Pass Before proceeding, watch the following video: <iframe width="560" height="315" src="https://www.youtube.com/embed/jckqXR5CrPI?si=8dQwOHu36-djRL8R&amp;start=46" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ### Step By Step Flutter's layout system follows a strict parent-down, child-up flow: 1. A widget gets its constraints from its parent. 1. Then the widget goes through its own list of children. One by one, the widget tells its children what their constraints are, and then asks each child what size it wants to be. 1. Then, the widget positions its children (horizontally in the x-axis, and vertically in the y-axis), one by one. 1. Finally, the widget tells its parent about its own size (within the constraints). --- ### Limitations Flutter's layout engine is designed to be a one-pass process. This results in a few limitations: 1. A widget can decide its size only within the constraints given to it by its parent. This means **a widget usually can't have any size it wants**. 2. A widget can't know and **doesn't decide its own position** in the screen, since it's the widget's parent who decides the position of the widget. 3. Since the parent's size and position, in its turn, also depend on its parent, it's impossible to precisely define the size and position of any widget without **taking into consideration the tree as a whole**. 4. If a child wants a different size from its parent and the parent doesn't have enough information to align it, then the **child's size might be ignored**. --- ## Main Features of the Layout Process ### Child can propose, parent may impose Although the children widgets are allowed to propose a specific size, they are forced to obey their parents’ constraints; if not, the proposed size will be ignored, and the constraints will be imposed! #### Children can "wish." A child widget can request a specific size (e.g., `Container(width: 200)`), but this is merely a suggestion. It may be overridden if it violates constraints, which are the law. **Example:** If the parent’s maxWidth is `100`, a child’s `width: 200` will be clamped to `100`. If the parent’s minHeight is `200`, a child’s `height: 50` will be clamped to `200`. <iframe src="https://dartpad.dev/?id=d426317eb57698e6aea9c6bfd68505bc" width="100%" height="620" frameborder="0" ></iframe> #### Parents have absolute authority The parents' constraints are non-negotiable. If a child’s desired size doesn’t fit, the parent forces the child to resize. If the child refuses (e.g., Text overflow), you’ll see a yellow/black **overflow** warning. <iframe src="https://dartpad.dev/?id=3d3200ba2c53b6fa5a655ca62eff1c33" width="100%" height="620" frameborder="0" ></iframe> ## The Critical Link Between Sizing and Layout *You may ask, "**Why constraints and sizes first matter?**"* Because Flutter uses dimensions for precise positioning! The child’s actual size (post-constraint enforcement) is reported to the parent, and the parent then uses this size for positioning (e.g., centering, aligning in a Row). A widget’s size directly determines where it’s placed. Until a child reports its size, the parent may not position it correctly. **Following are examples of why sizing is non-negotiable for accurate layouts:** ### 1. Sizes Dictate Alignment Example: Centering a widget requires knowing its dimensions. ``` dart Center( // Parent needs the child’s size to center it child: Container(width: 100, height: 100), ) ``` The Center widget uses the child’s 100×100 size to calculate alignment offsets. --- ### 2. Sizes Enable Relative Positioning Example: Positioning in Stack relies on size. ``` dart Stack( children: [ // Positions will depend on this size Container(color: Colors.grey, width: 100, height: 100), Positioned( left: 10, // 10px from left edge bottom: 10, // 10px from top edge child: Container(width: 50, height: 50, color: Colors.blue), ), ], ) ``` The Stack here uses the first non-positioned child's 100×100 size to apply the left: 10, top: 10 offset. Try to remove the grey container and see how the position will differ. ![alt-text-1](https://hackmd.io/_uploads/rJAehdDlxg.png =32%x) ![alt-text-2](https://hackmd.io/_uploads/H1JK2dDxxg.png =30%x) In the next sections, you'll understand the reason behind the new behavior! --- ### 3. Parent Layouts Depend on Children's Sizes For example, Row and Column space children based on their sizes. ``` dart Row( children: [ Container(width: 50, color: Colors.red), // Contributes 50px Container(width: 100, color: Colors.blue), // Contributes 100px ], ) ``` The Row here sums the children’s widths (50 + 100) to determine its total size and its axes alignments. ## A real Scenario ![children](https://hackmd.io/_uploads/HkHlprLleg.png) The negotiation goes something like this: **Column**: "Hey parent, what are my constraints?" **Parent**: "You must be from `0 to 300 pixels wide, and 0 to 85 tall`." **Column**: "Hmmm, since I want to have `5 pixels of padding`, then my children can have at most `290 pixels of width and 75 pixels of height.`" **Column**: "Hey first child, You must be from `0 to 290 pixels wide, and 0 to 75 tall`." **First child**: "OK, then I **wish** to be `290 pixels wide, and 20 pixels tall.`" **Column**: "Hmmm, since I want to put my second child below the first one, this leaves `only 55 pixels of height for my second child`." **Column**: "Hey second child, You must be from `0 to 290 wide, and 0 to 55 tall.`" **Second child**: "OK, I wish to be `140 pixels wide, and 30 pixels tall`." **Column**: "Very well. `My first child has position x: 5 and y: 5, and my second child has x: 80 and y: 25.`" **Column**: "Hey parent, I've decided that my size is going to be `300 pixels wide, and 60 pixels tall`." # Part 2: Constraints Variants As mentioned, constraints act as strict boundaries that determine how large or small a widget can be. A widget's size respects a BoxConstraints if, and only if, all of the following relations hold: > minWidth <= Size.width <= maxWidth > minHeight <= Size.height <= maxHeight The constraints themselves must satisfy these relations: > 0.0 <= minWidth <= maxWidth <= double.infinity > 0.0 <= minHeight <= maxHeight <= double.infinity Here’s a breakdown of all possible variations of constraints: ## 1. Tight constraints A tight constraint has equal minimum and maximum, which means the widget is forced to use exactly that size. ### Characteristics: * Forces the child to a specific size. * The most explicit type of constraints. * Child has no flexibility in sizing. ### Common sources #### 1. Explicit fixed sizes: ``` dart SizedBox( width: 100, height: 100, // Tight in both axes child: Container( height: 10, //will be ignored color: Colors.amber), ) ``` #### 2. Parent enforcement: ``` dart Container(width: 200) // Creates tight width constraint ``` #### 3. Flex widgets in expanded space: ``` dart Expanded(child: ...) // Creates tight constraints in flex axis ``` Run the following app and see the magic! <iframe src="https://dartpad.dev/?id=b059c4f0720b66d39d348a94c59ad242" width="100%" height="620" frameborder="0" ></iframe> ## 2. Loose constraints A loose constraint has a minimum of zero and a maximum of non-zero. The child can loosely choose any size from zero up to the maximum. <div style="text-align:center"><img width="50%" src="https://hackmd.io/_uploads/SyyMTuIege.png" /></div> ### Characteristics: * Permitting sizes shrink down to zero. * More dynamic layout behavior. ### Common sources #### 1. Explicit loose constraints: ``` dart BoxConstraints.loose(Size(200, 100)) ``` This will result in the following constraints: > constraints: BoxConstraints(0.0<=w<=200.0, 0.0<=h<=100.0) #### 2. Alignment-based wrappers: Align and Center, which loosen the constraints given to the child rather than removing them entirely. ``` dart Align(alignment: Alignment.bottom, child: ...) ``` So, if needed, the tight can be easily loosened! Run now and see the difference. <iframe src="https://dartpad.dev/?id=cce8766091869752b0488dbe190195fd" width="100%" height="620" frameborder="0" ></iframe> #### 3. Flexible widgets: ``` dart Flexible(child: ...) // Creates loose constraints in flex axis ``` Run the following code and see the differences between Expanded and Flexible: <iframe src="https://dartpad.dev/?id=a89f36f08ad71f6a2e5e28db933e79b4" width="100%" height="620" frameborder="0" ></iframe> You may have noticed the `space at the bottom of the column`; this is because Flutter first divides space among the flex factors. So, even for `Flexible` with loose fit and allowing the blueGrey container to shrink, its flex is still counted during the initial division of space, and so this space comes in! Try to wrap the red Container with the `Center` widget, and run to see how the `Center` will allow it to shrink! ## 3. Bounded constraints An axis whose maximum constraint is finite is bounded. ### Characteristics: * Provides maximum flexibility within bounds. * The child determines its preferred size up to the limit. * The child can't grow beyond bounds. * Out-of-bound parts may be hidden or require special handling. ### Examples #### 1. ListView inside SizedBox The following ListView can scroll its content, but visually stays within 300 pixels in height. ``` dart ConstrainedBox( constraints: BoxConstraints( maxHeight: 300, // This make ListView bounded vertically ), child: ListView( ... ), ), ) ``` #### 2. Stack with overflow handling Run the following example and notice the red box, which goes out of the **Stack**'s bounds(200*200), but depending on the stack behavior, it is clipped. Change the `Stack`'s `clipBehavior` property to `Clip.none` and see the difference. <iframe src="https://dartpad.dev/?id=15e7769ca6221d259579b2357eb17b82" width="100%" height="620" frameborder="0" ></iframe> Play around, try to remove the parent SizedBox, and notice how the Stack changes, then try to set the height and width for the amber container and see the difference. As you see, Stack sizes itself to include all of its non-positioned children; if this is missed, it gets its size (bounds) from its parent. If both are missed, the layout may fail; it may be given unbounded constraints while left with no size. Continue to understand what is said. ## 4. Unbounded constraints An axis whose maximum constraint is infinite is called unbounded. With unbounded constraint, there is no upper limit; the child can grow as big as it wants and can be any size. <div style="text-align:center"><img width="50%" src="https://hackmd.io/_uploads/H1tn87Dxex.png" /></div> ### Characteristics: * Used in scrollable layouts (e.g., `SingleChildScrollView` and `ListView`). * Unbounded can't directly be a child of bounded, unless bounded explicitly. * The parent relies on the child to pick their own size. If the child doesn’t choose a size, Flutter throws an error → e.g., `Flutter RenderBox was not laid out`. * If a child’s size depends on the parent’s constraint, and the child expects finite, you’ll get the following layout error → `BoxConstraints forces an infinite height`. ### Some tricky cases #### 1. ListView inside a Column If you place a ListView(unbonded) inside a Column(bounded) without giving it a bounded height, Flutter will report this error: > "Vertical viewport was given unbounded height." So, we need to make the ListView vertically bounded, how? **There are two common solutions:** * Wrap the ListView with a SizedBox with a finite height: ``` dart SizedBox( height: 300, child: ListView(...), ) ``` * Or wrap it with an Expanded widget, which gives it the remaining space: ``` dart Expanded( child: ListView(...), ) ``` Run the following code, and solve the bug: <iframe src="https://dartpad.dev/?id=2d43a87fa9585b82ca065677c6e7badb" width="100%" height="620" frameborder="0" ></iframe> --- #### 2. Column inside SingleChildScrollView ``` dart SingleChildScrollView( child: Column( children: List.generate( 50, (index) => ListTile(title: Text('Item $index')), ), ), ) ``` The SingleChildScrollView gives unbounded height to its child, so the Column can grow infinitely tall based on its children. Here, there are no errors because the Column can size itself by summing its children. Errors come when you place widgets that demand the parent to tell them their sizes (they don’t pick a size on their own). For example, widgets like Expanded, Flexible, or a vertical ListView require a bounded height to know how much space to take. Continue to see those cases! --- #### 3. Expanded inside Column inside SingleChildScrollView ``` dart SingleChildScrollView( child: Column( children: [ Expanded( //needs bounded height, but gets infinite child: Container(color: Colors.blue), ), ], ), ) ``` `Expanded` expects bounded constraints to tighten its child to fill available space along the main axis. Here, the available height is infinite → causes the following Flutter layout failure: > RenderFlex children have non-zero flex, but incoming height constraints are **unbounded**. ##### You may think of `Expanded` like this: > "I'll stretch my child to exactly fill all the remaining space... > ...but only if someone tells me how much space there is!" If the available space is infinite (unbounded), `Expanded` says: > "I can't tighten — I don't know the limit!" ##### Other widgets that fail in unbounded constraints: * `Flexible`: like `Expanded`, it needs bounded space. * `SizedBox.expand()`: Expects finite max width/height to expand and fill it. --- #### 4. Constraints Cycle It happens when a parent gives their child unbounded constraints, e.g, height, then this child chooses to take all the height their parent gives by saying: `height: double.infinity`. ``` dart SingleChildScrollView( child: Container( height: double.infinity, // take all the height my parent gives color: Colors.blue, ), ), ``` In the above example: > * Parent says: "Child, you can be as tall as you want (infinite)." > * Child says: "I'll be as tall as you can give (infinite)." So, it results in the following error: > BoxConstraints forces an infinite height. ##### Solution: Break the cycle The cycle will be broken by setting a fixed height or wrapping it with a ConstrainedBox with a finite maxHeight. ``` dart SingleChildScrollView( child: Container( height: 500, //now Flutter can calculate the layout safely color: Colors.blue, ), ), ``` So, never use `double.infinity` in the **main axis** of scroll views or rows/columns. ## 5. Expanding constraints An axis is expanding if it is **tightly infinite**, its minimum and maximum constraints are both infinite. Here, parent forces child to take all available space (often via tight constraints), so expanding is not optional; it is a must. ### BoxConstraints.expand() This constructor will create box constraints that expand to fill another box. If width or height is given, the constraints will require **exactly** the given value in the given dimension. ``` dart const BoxConstraints.expand({double? width, double? height}) : minWidth = width ?? double.infinity, maxWidth = width ?? double.infinity, minHeight = height ?? double.infinity, maxHeight = height ?? double.infinity; ``` ### Example1: Root widget The most common example of expanding constraints is the `root` widget of your app. It forces its child to exactly match the size of the screen. ``` dart void main() { runApp( MaterialApp( title: 'Expanding Constraints', home: Container(color: Colors.blue, height: 100), ), ); } ``` Here, the screen (through MaterialApp) applies expanding constraints, forcing the Container to expand and fill the screen. ### Example2: AppBar widget When you place an AppBar inside a Scaffold, it automatically expands to fill the entire width of the screen because it receives expanding constraints along the horizontal axis. ``` dart Scaffold( appBar: AppBar( title: Text('Expanding Width Example'), backgroundColor: Colors.green, ), ) ``` # Part 3: Boxes behind Widgets ## RenderBox: Flutter’s core layout engine In Flutter, widgets are rendered by their underlying [`RenderBox`](https://api.flutter.dev/flutter/rendering/RenderBox-class.html) objects. <div style="text-align:center"><img width="70%" src="https://hackmd.io/_uploads/ryZbHwDexg.png" /></div> These RenderBoxes handle layout by receiving constraints from their parents and deciding how big they want to be. Again, in determining its size, the child must respect the constraints given to it by its parent. The basic constraint flow, in short, is: * Boxes pass their constraints down to their children. * Children decide their size within those constraints and report back up. Additionally, the RenderBox handles the actual painting of the widget onto the screen, taking care of details that need to be applied. ### RenderBox implementers The RenderBox class serves as a base class in Flutter's rendering system and has many implementers, each providing different layout and painting behaviors depending on the widget's purpose. Examples include: 1. **RenderFlex**: used for flex-based layouts like Row and Column 2. **RenderFlow**: supports custom flow-based layouts 3. **RenderImage**: handles the layout and painting of images 4. **RenderStack**: used for layered layouts like Stack 5. **RenderTable**: manages tabular layouts, as used by Table. ## Main Box Types Here are three main types of boxes (by their sizing behavior): ### 1. Expand-to-Fill Boxes Boxes that try to be as big as possible. They expand to fill the available space. #### Examples The boxes used by: 1. Center 2. ListView 3. Container by default with no width/height/child ### 2. Shrink-Wrapping Boxes Boxes that size themselves to match their child and the content's size. #### Examples: The boxes used by: 1. ColoredBox 2. Padding 3. Transform 4. Opacity 5. SizedBox.shrink() In essence, many widgets contain the property `shrinkWrap` to enable them to occupy only the space necessary to hold their children. For example: ``` dart ListView( shrinkWrap: true, children: <Widget>[ ListTile(title: Text('Item 1')), ListTile(title: Text('Item 2')), ListTile(title: Text('Item 3')), ], ) ``` According to the Flutter documentation, using shrinkWrap in lists is expensive when you have many items; it’s fine just for small lists (like inside dialogs, forms). ### 3. Intrinsic-Size Boxes Boxes that try to be a specific size. They do not expand to fill the parent, and they also don’t shrink-wrap a child (because they are the leaf). They size themselves based on their intrinsic content (e.g., text length, image resolution, icon size). #### Examples: 1. Image 2. Text 3. Icon ## Boxes with Multiple Behaviors Some boxes in Flutter behave differently based on the constraints they receive and how the corresponding widgets are constructed. ### RenderFlex Boxes RenderFlex is one of the classes that implements the RenderBox in Flutter’s rendering pipeline. It provides a specific layout logic for flex-based layouts, such as those used by Row and Column. A flex box behaves differently depending on whether its constraint is bounded or unbounded in its primary and cross directions. #### 1. Primary direction * **With a bounded constraint**: Tries to be as big as possible. * **With an unbounded constraint**: Tries to fit its children in that space. In this case, each child's flex value must be set to zero, meaning that you can't use Expanded when the flex box is inside another flex box or a scrollable; otherwise, it throws an exception, as you see earlier. #### 2. Cross direction **Official doc says:** > It must never be unbounded, or it can't reasonably align its children. **What it really means (practical):** A `Row` or `Column` (a `Flex`) can receive **unbounded constraints** on its **cross axis** in some widget trees, especially when it is placed inside a **scrollable**. When the cross-axis constraint is unbounded, the `Flex` cannot know a finite “available” cross size, so it typically **shrink-wraps** to fit its children on that axis. In this situation, alignments like `start/center/end` may show little or no visible effect, and `CrossAxisAlignment.stretch` (or any behavior that tries to make children fill the cross axis) can **assert/fail**, because stretching would require expanding children to an **infinite** size, which is impossible. **Rule of thumb:** **Cross axis must be finite for stretch/fill and meaningful alignment.** And, the danger combo is **unbounded cross axis + stretch/fill**, because this needs a finite cross extent to compute a tight constraint for children. See the following example: <iframe src="https://dartpad.dev/?id=98e8f132433bc17eefe6254a42144753" width="100%" height="620" frameborder="0" ></iframe> Layout for a Flex proceeds in six steps. If you're interested in learning them, refer back to this [doc](https://api.flutter.dev/flutter/widgets/Flex-class.html#layout-algorithm). --- ### RenderStack Boxes RenderStack is another implementation of RenderBox, the box behind the Stack. The Stack's box is sized to enclose all of its non-positioned children, meaning it acts as a shrink-wrapping box. If there are no non-positioned children, it attempts to expand to be as big as possible. ## Boundaries Pushers In Flutter, layout constraints act like invisible fences; most widgets stay neatly within their bounds. But sometimes, they need to break these parental limits. Common widgets that are used when you want intentional overflow effects: OverflowBox and UnconstrainedBox. They bypass parent constraints completely. See the following example: <iframe src="https://dartpad.dev/?id=c24f87982d70f1bf685226f97a693f82" width="100%" height="620" frameborder="0" ></iframe> As you see, the OverflowBox imposes its own constraints of maxWidth and maxHeight of 200 pixels on its child, which allows the child to overflow the parent container. Without the OverflowBox, the child widget would be constrained to the size of the parent container and would not overflow the parent container. ## How to know the layout rules of specific widgets As Flutter's documentation says: > Knowing the general layout rule is necessary, but it's not enough. > > Each widget has a lot of freedom when applying the general rule, so there is no way of knowing how it behaves by just reading the widget's name. > > If you try to guess, you'll probably guess wrong. You can't know exactly how a widget behaves unless you've read its documentation or studied its source code. # Examples ## Long Unconstrained Text (Overflow issue) In the code below, we're using a `ListView.builder` to create a list of items, each containing a Row with a Circular Container and a Container that holds a Text widget. The issue arises when the Text widget contains a long text that will overflow and cause layout issues. <iframe src="https://dartpad.dev/?id=a7970de2b04be5e50bc084aae7cdfd87" width="100%" height="620" frameborder="0" ></iframe> ### The Solution To solve this issue: Wrap the overflowing Text with a constrained box, or here, use the constraints of the holder container by setting its `maxWidth`. To do this, just uncomment the maxwidth and see the difference. By setting the maxWidth, the Container will be constrained by the screen width, preventing the Text from overflowing. The text will now wrap within the available width, ensuring a clean layout without overflow issues. --- ## TextField inside Row with no/unbounded width `TextField` is one of the widgets that can't choose a size for itself; it relies on its parent and the given constraints. The Row, in its turn, doesn't specify sizes for its children; it relies on them to choose their own sizes within the constraints, then compute its total size based on them. <iframe src="https://dartpad.dev/?id=60b2fcb01723079f69e679c815421756" width="100%" height="620" frameborder="0" ></iframe> In the case given above, the TextField is not given a size or finite constraints, so Row can't compute its size, resulting in the common error: > NEEDS-LAYOUT NEEDS-PAINT > constraints: BoxConstraints(unconstrained) > size: MISSING ### The Solution The error message indicates that `if the InputDecorator is contained by a Row, its width must be constrained`. To resolve this, you can use an `Expanded` widget or a `SizedBox` to constrain the width of the InputDecorator or the TextField that contains it. Now, wrap the TextField widgets in an Expanded widget. This will ensure that they take up the available space and are properly constrained, and then can be laid out. ``` dart Row( children: [ Expanded( child: TextField( ... ), ), SizedBox(width: 10), Expanded( child: TextField( ... ), ), ], ), ``` # Challenge: Beyond the rule **Think, what if you try to get the following layout?** ![Screenshot 2025-05-08 112237](https://hackmd.io/_uploads/rkvxc19xxe.png) **Here is the base code, try to do it:** <iframe src="https://dartpad.dev/?id=1682f96fac1eb0adcf0dd9272d1b6aa7" width="100%" height="620" frameborder="0" ></iframe> Now you are saying: >***Oh! What happens when a widget’s size depends on its siblings?!*** Normally, in Flutter's layout model, each widget sizes itself independently based on the constraints from its parent. But sometimes, we want siblings to coordinate, like the Align widgets in the above case, making all items in the row match the height of the highest one. Here’s where Intrinsic Dimensions come into play. ## Intrinsic dimensions The intrinsic dimensions of a widget are the minimum width and height needed to render its content without clipping or overflow. For example, the intrinsic height of a Text widget is the height it needs to fully show its text. ## Intrinsic Sizing Process To resolve sibling-dependent sizing, Flex layouts follow this intrinsic process: 1. The parent asks each child: ***If you had no siblings and could size yourself freely, what’s your ideal size?*** 2. Each child reports its intrinsic size. 3. The parent then enforces the largest reported size uniformly across all children, ensuring consistent alignment. ## The Solution To enable this behavior in the above Row, wrap it with an `IntrinsicHeight` widget. Putting a `Row` inside an `IntrinsicHeight` will allow all `Row` children to be as tall as the tallest child, which is our case. Additionally, `IntrinsicHeight` is useful when the parent offers unbounded height, and you want a child, which might otherwise attempt to expand infinitely, to size itself to a natural, reasonable width instead. ## Another Scenario! Return to the above example, remove your added IntrinsicHeight widget, and replace the Center with a ListView widget. The body code is now as follows: ``` dart body: ListView( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ ... ], ), ], ), ``` If you run the code now, you will get a shrink-wrapper Row. This is the behavior of Row if it gets unbounded constraints, isn't it? ![cons](https://hackmd.io/_uploads/Sy4qx10ell.png) You noticed now how the row passes the unbounded constraints, so even though each Align widget is trying to position its child according to the alignment property, the unbounded constraints will impact the final sizing and positioning as follows! ![intrinsic](https://hackmd.io/_uploads/rknMe1Allg.png) ### The Solution Yes, yes. Exactly as you think, this case also requires an **`IntrinsicHeight`** around the Row, which will give the Align widgets breath based on the tallest sibling's height. ``` dart body: ListView( children: [ IntrinsicHeight( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ ... ], ), ), ], ) ``` In this case, the height of the tallest child will compensate for the absence of constraints and bounds, giving the Align widgets breath based on the highest sibling's height, and so the effect of their alignment properties will be shown. Now, inspect and see the difference! ![conn](https://hackmd.io/_uploads/rkG7N10lll.png) ## Performance note The intrinsic process is more expensive than a regular layout as it triggers multiple passes. Therefore, it’s ideal for small layouts but should be avoided in performance-sensitive areas, such as large scrolls. # Comprehensive Examples For an interactive experience, run the Code below: <iframe src="https://dartpad.dev/?id=45d687eb9de7ed10737353affd2b4192" width="100%" height="620" frameborder="0" ></iframe> # References 1. [Understanding Constraints](https://docs.flutter.dev/ui/layout/constraints) 2. [Common Errors](https://docs.flutter.dev/testing/common-errors) 3. [Box Constraints](https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html) 4. [RenderBox](https://api.flutter.dev/flutter/rendering/RenderBox-class.html) 5. [Flex](https://api.flutter.dev/flutter/widgets/Flex-class.html) 6. [Render Stack](https://api.flutter.dev/flutter/rendering/RenderStack-class.html) 7. [OverflowBox](https://api.flutter.dev/flutter/widgets/OverflowBox-class.html) 8. [IntrinsicHeight](https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html) 9. [Intrinsic widgets](https://www.youtube.com/watch?v=Si5XJ_IocEs) # What's next Now that you understand constraints, you can better understand how responsive widgets work. Follow this [link](https://hackmd.io/@aelaydi/flutter-resposive-p1) to visit the Responsive UI workshop.