Flutter Layout - Understanding Constraints

When someone who learns Flutter asks why width:100your widget is not 100 pixels wide, the default answer is to tell him that the widget is placed in the Center, right? (I don't know the reason, the answer is irrelevant)

never do that

If you do that, they'll come back again and again, asking why something FittedBoxisn't working, why Columnis it overflowing, or IntrinsicWidthwhat should be done?

Instead, first tell them that Flutter layouts are very different from HTML layouts (this is probably where they came from), and then ask them to remember the following rules:

Constraints go down. Sizes go up. Parent sets position 

If you don't understand this rule, you can't really understand the layout of Flutter, so Flutter development should learn early:

more specifically:

  • Widgets get their own Constraints from their parent. Constraints are just a set of 4: minimum and maximum width, and minimum and maximum height.
  • The widget then iterates over its own sublist. One by one, the widget tells its children what their constraints are (each child may be different), and then asks each child what size it wants.
  • The widget then positions its children one by one (horizontally on the x-axis and vertically on the y-axis).
  • Finally, the widget communicates its own size to its parent (within the original limits, of course).

For example, if a composite widget like you contains a column with some padding, and you want to lay out its two children like this:

The negotiation looks like this:

  • Widget : "Hey Parent, What Are My Limitations?"
  • Parent : "Your width must be between 80to 300pixels and your height must be between 30to 85pixels."
  • Widget : "Well, since I want 5a padding of pixels, my child can be at most 290a pixel wide and 75a pixel high."
  • Widget : " Hey first child, your width must be 0between 290200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
  • First Child : "Okay, then I want wide 290pixels, high 20pixels."
  • Widget : "Well, since I want to place my second child below the first, my second child is only left with the 55height in pixels."
  • Widget : "Hey dick, your width must be 0between 290to and your height must be 0between 55to."
  • Second child : "OK, I want wide 140pixels, high 30pixels."
  • Widget : "Good. My first child's position is x:5sum y:5, and my second child's position is x:80sum y:25."
  • Widget : "Hey Parent, I've decided that my dimensions will be 300pixels wide by 60pixels tall."

1. Restrictions

Due to the layout rules mentioned above, Flutter's layout engine has several important limitations:

  • A widget can only determine its own size within the constraints just given by its parent. This means that a widget usually cannot have any size it wants .
  • A widget cannot know or determine its own position on the screen , because it is the widget's parent that determines the widget's position.
  • Since the parent's size and position are in turn dependent on its own parent, it is impossible to precisely define the size and position of any widget without considering the entire tree.
  • If a child wants a different size than its parent, and the parent doesn't have enough information to tell the other, the child's size may be ignored. Be specific when defining alignment .

1.1. Examples

The following are 29 examples for the above, please read it slowly, and you will have a better understanding of Contraints after reading.

1.1.1, Example 1

Container(color: red) 

Screen is Containerthe parent of , which enforces being Containerexactly the same size as the screen.

So Containerwill fill the screen and paint it red.

1.1.2, Example 2

Container(width: 100, height: 100, color: red) 

The red one Containerwants to 100x100, but it can't because the screen forces it to be exactly the same size as the screen.

So the container fills the full screen.

1.1.3, Example 3

Center(child: Container(width: 100, height: 100, color: red),
) 

The screen is forced Centerto be exactly the same size as the screen, so Center fills the screen.

CenterTell Containerit to be any size it wants, but not larger than the screen. Now Contaiinerit can be 100x100.

1.1.4, Example 4

Align(alignment: Alignment.bottomRight,child: Container(wiidth: 100, height: 100, color: red)
), 

This differs from the previous example, which uses Aligninstead of Center.

AlignAlso tell Containerit can be any size it wants, but it won't center it if there is empty space Container. Instead, it will Containersnap to the bottom right corner of the available space.

1.1.5, example 5

Center(child: Container(width: double.infinity, height: double.infinity, color: red)
) 

The screen is forced Centerto be exactly the same size as the screen, so Centerit fills the screen.

CenterTell Containerit to be any size it wants, but not larger than the screen. ContainerWants to be infinitely large, but since it can't be larger than the screen, it can only fill the screen.

1.1.6, Example 6

Center(child: Container(color: red),
) 

The screen is forced Centerto be exactly the same size as the screen, so Centerit fills the screen.

CenterTell Containerit to be any size it wants, but not larger than the screen. Since Containerthere are no children and no fixed size, it decides to be as big as possible, so it fills the entire screen.

But why Containerwas it decided this way? Simply because it was Containera design decision by those who created the widget. It may be created differently, and you'll have to read Containerthe documentation to understand how it behaves, depending on the situation.

1.1.7, Example 7

Center(child: Container(color: red,child: Container(color: green, width: 30, height: 30)),
) 

The screen is forced Centerto be exactly the same size as the screen, so Centerit fills the screen.

CenterTells red Containerit can be any size it wants, but no larger than the screen. Since red Containerhas no size but has a subcontainer, it decides to be the same size as its subcontainer.

Red Containertells its children that it can be any size it wants, but not bigger than the screen.

The child is a green one Containerand it wants to be 30 x 30. Given that red Containeritself is the same size as its child, it 30 x 30is also invisible to red because green Containercompletely covers the red container.

1.1.8, Example 8

Center(child: Container(padding: const EdgeInsets.all(20.0),color: red,child: Container(color: green, width: 30, height: 30)),
) 

Red Containerresizes itself to the size of its children, but it takes its own padding into account. So it is also 30 x 30plus padding. Because of the padding, the red courseware and the green Containerare the same size as in the previous example.

1.1.9, Example 9

ConstrainedBox(constraints: const BoxConstraints(minWidth: 70,minHeight: 70,maxWidth: 150,maxHeight: 150,),child: Container(color: red, width: 10, height: 10)
) 

You might guess Containerit has to be between 70 and 150 pixels, but you'd be wrong. Only imposes additionalConstrainedBoox constraints on those received by its parent .

Here, the screen is forced to ConstrainedBoxbe exactly the same size as the screen, so it tells its child Containerto also take the size of the screen, thereby ignoring its constraint parameter.

1.1.10, Example 10

Center(child: ConstrainedBox(constraints: const BoxCoonstraints(minWidth: 70,minHeight: 70,maxWidth: 150,maxHeigght: 150,),child: Container(color: red, width: 10, height: 10)),
) 

Now, Centerthe allowed ConstrainedBoxsize does not exceed the screen size. ConstrainedBoxImposes additional constraints on its constraintsarguments to its children .

ContrainterMust be between 70 and 150 pixels. It wants to have 10 pixels, so it ends up with 70 (the minimum).

1.1.11, Example 11

Center(child: ConstrainedBox(constraints: const BoxConstraints(minWidth: 70,minHeight: 70,maxWidth: 150,maxHeight: 150,),child: Container(color: red, width: 1000, height: 1000)),
) 

CenterThe allowed ConstrainedBoxsize does not exceed the screen size. ConstrainedBoxAdditional constraints on the parameter are imposed on its children.

ContainerMust be between 70 and 150 pixels. It wants to have 1000 pixels, so it ends up with 150 (max).

1.1.12, Example 12

Center(child: ConstrainedBox(constraints: const BoxConstraints(minWidth: 70,minHeight: 70,maxWidth: 150,maxHeight: 150,),child: Container(color: red, wiidth: 100, height: 100)),
), 

CenterThe allowed ConstrainedBooxsize does not exceed the screen size. ConstrainedBoxImposes additional constraints on its constraintsarguments on its children.

ContainerMust be between 70 and 150 pixels. It wants 100 pixels, and that's how big it is because it's between 70 and 150.

1.1.13, Example 13

UnconstrainedBox(child: Container(color: red, width: 20, height: 50)
) 

The screen is forced UnconstrainedBoxto be exactly the same size as the screen. However, UnconstrainedBoxallow its child Containerto be any size it wants.

1.1.14, Example 14

UnconstrainedBox(child: Container(color: red, width: 4000, height: 50),
) 

Screen forces UnconstrainedBooxthe exact size of the screen, while UnconstrainedBoxletting its children Containerbe any size it wants.

Unfortunately, in this case, Containerthe width of is 4000px and is too large to fit UnconstrainedBox, so UnconstrainedBoxthe very dreaded "overflow warning" is displayed.

1.1.15, Example 15

OverflowBox(minWidth: 0.0,minHeight: 0.0,maxWidth: double.infinity,maxHeight: double.infinity,child: Container(color: red, width: 4000, height: 50)
) 

Screen forces OverflowBoxthe exact size of the screen, while OverflowBoxallowing its child Containerto be any size it wants.

OverflowBoxSimilar to UnconstrainedBox; except that it won't show any warning if the child doesn't fit in the space.

In this example, Containerthe width of is 4000 pixels, which is too large to fit OverflowBox, but OverflowBoxjust displays as much as possible without warning.

1.1.16, Example 16

UnconstrainedBox(child: Container(color: Colors.red, width: doouble.infinity, height: 100)
) 

This won't render anything, you'll see an error in the console.

UnconstrainedBoxAllows its children to be of any size, but its children are containers of unlimited size.

Flutter cannot render infinite size as it throws an error with the following message: BoxConstraints forces an infinite width.

1.1.17, Example 17

UnconstraintedBox(child: LimitedBox(maxWidth: 100,child: Container(color: Colors.red,width: double.infinity,height: 100,),),
) 

You won't get an error here anymore, because when LimitedBoxis UnconstrainedBoxgiven infinity; it's about to pass a max-width of 100 down to its children.

If you UnconstrainedBoxswap out to Centera widget, LimitedBoxits limit is no longer applied (since its limit is only applied when it gets infinite constraints), and Containerthe width of the widget can grow beyond 100.

This explains the difference between LimitedBoxand .ConstrainedBox

1.1.18, Example 18

const FittedBox(child: Text('Some Example Text.'),
) 

The screen is forced FittedBoxto be exactly the same size as the screen. Text has some natural width (also known as its intrinsic width), which depends on the amount of text, font size, etc.

FittedBoxArbitrary size is allowed Text, but when Texttold its size FittedBox, FittedBoxscales Textuntil it fills all available width.

1.1.19, Example 19

const Center(child: FittedBox(child: Text('Some Example Text.'),),
) 

But what happens if you FittedBoxput it inside a widget? Any size is allowed , up to screen size.CenterCenterFittedBox

FittedBoxIt then resizes itself according to the text, and makes the text be whatever size it wants. Since FittedBoxand Texthave the same size, no scaling occurs.

1.1.20, Example 20

const Center(child: FittedBox(child: Text('This is some very very very large text that is too big to fit a regular screen in a single line.')),
) 

But FittedBoxwhat happens if the text is too large to fit the screen while inside the center widget?

FittedBoxTrying to size itself according to the text, but it can't be larger than the screen. It then assumes the screen size to make it fit the screen too.

1.1.21, Example 21

const Center(child: Text('This is some very very very large text thass is too big to fit a regular screen in a single line.')
) 

However, if you remove FittedBox, Textit takes its largest width from the screen and breaks the line to fit the screen.

1.1.22, Example 22

FittedBox(child: Container(height: 20.0,width: double.infinity,color: Colors.red,),
) 

FittedBoxOnly bounded widgets (with non-infinite width and height) can be scaled. Otherwise, it won't render anything and you'll see an error on the console.

1.1.23, Example 23

Row(children: [Container(color: red, child: const Text('Hello!', style: big),Container(color: green, child: const Text('Goodbye!', style: big),],
) 

The screen is forced Rowto be exactly the same size as the screen.

Like one UnconstrainedBox, Rowdoesn't impose any constraints on its children, but lets them be whatever size they want. RowThey are then placed side by side, any extra space left blank.

1.1.24, Example 24

Row(children: [Container(color: red,child: const Text('This is a very long text that ''won\'t fit the line.',style: big,),),Container(color: green, child: const Text('Goodbye!', style: big))],
) 

Since Rowno constraints are imposed on its children, the children are likely to be too large to fit in the Rowavailable width, as in UnconstrainedBox, Rowan "overflow warning" is displayed.

1.1.25, Example 25

Row(children: [Expanded(child: Center(child: Container(color: red,child: const Text('This is a very long text that won\'t fit the line.',style: big,),),),),Container(color: green, child: cont Text('Goodbye!', style: big))],
) 

When a Rowchild of a widget is wrapped in a Expandedwidget, Rowit will no longer let the child define its own width.

Instead, it defines the expanded width in terms of the other children, and only then, the expanded widget forces the original child to have the expanded width.

In other words, once you use Expanded, the original child's width becomes irrelevant and is ignored.

1.1.26, Example 26

Row(children: [Expanded(child: Container(color: red,child: const Text('This is a very long text that won\'t fit the line.',style: big,),),),Expanded(child: Container(color: green,child: const Text('Goodbye!',style: big,),),),],
) 

If Rowall children of the widget are contained within Expandedthe widget, each Expandedhas a size flexproportional to its parameter, only then each Expandedwidget enforces the width its children have Expanded.

In other words, Expandedthe preferred width of its children is ignored.

1.1.27, Example 27

Row(children: [Flexible(child: Container(color: redmchild: const Text('This is a very long text that won\'t fit the line.',style: big,),),),Flexible(child: Container(color: green,child: const Text('Goodbye!',style: big,),),),],
) 

The only difference if you use Flexibleinstead is that hi allows its children to have the same or smaller width than itself, while forcing its children to have the exact same width as itself. But both ignore the child's width when resizing itself.ExpandedFlexibleFlexibleExpandedExpnandedExpandedFlexible

注意:这意味着不可能按比例扩展`Row`子元素的大小。当您使用`Expanded`或`Flexible`时,`Row`要么使用确切的子宽度,要么完全忽略它。 

1.1.28, Example 28

Scaffold(body: Container(color: blue,child: Column(children: const [Text('Hello!'),Text('Goodbye!'),],),),
) 

The screen is forced Scaffoldto be exactly the same size as the screen, so Scaffoldit fills the screen. ScaffoldTell Containerit to be any size it wants, but not larger than the screen.

注意:当一个小部件告诉它的孩子它可以小于某个尺寸时,我们说这个小部件向它的孩子提供松散的约束。稍后会详细介绍。 

1.1.29, example 29

Scaffold(body: SizedBox.expand(child: Container(color: blue,child: Column(children: const [Text('Hello!'),Text('Goodbye!),],),),),
) 

If you want Scaffoldthe child to Scaffoldbe exactly the same size as itself, you can use SizedBox.expanda child that wraps it.

注意:当一个小部件告诉它的孩子它必须有一定的大小时,我们说这个小部件向它的孩子提供了严格的约束。 

2. Tight constraints and loose constraints

It's common to hear that certain constraints are "tight" or "loose," so it's worth understanding what that means.

Strict constraints offer a possibility, exact size. In other words, a tight constraint's maximum width is equal to its minimum width, and its maximum height is equal to its minimum height.

If you go to Flutterthe box.dartdocumentation and search for BoxConstraintsthe constructor, you'll find the following:

BoxConstraints.tight(Size size): minWidth = size.width,maxWidth = size.width,minHeight = size.height,maxHeight = size.height; 

If you revisit example 2 above, it tells us that the screen forces red Containerto be exactly the same size as the screen. ContainerOf course screen does this by passing strict constraints to .

On the other hand, loose constraints and set a max width and height, but keep the widget as small as possible. In other words, loosely constrained minimum width and height are both zero:

BoxConstraints.loose(Size size): minWidth = 0.0,maxWidth = size.witdhminHeight = 0.0,maxHeight = size.height; 

If you revisit example 3, it tells us to center to make the red container smaller, but not larger than the screen. ContainerOf course, the center does this by passing loose constraints to . Ultimately, Centerthe real purpose of a is to convert the tight constraints it gets from its parent (the screen) into the loose constraints of its children ( Container).

3. Learn specific widgets and layout rules

Knowing the general layout rules is necessary, but not sufficient.

Each widget has a lot of latitude in applying the common rules, so there's no way to know what a widget will do just by reading its name.

If you try to guess, you will most likely guess wrong. You can't know exactly how a widget behaves unless you read its documentation or study its source code.

Layout source code is often complex, so it's best to just read the documentation. However, if you decide to explore the layout source code, you can easily find it using the IDE's navigation capabilities.

  • Find a column in your code and navigate to its source code. To do this Android Studio, IntelliJuse command+B (macOS)or or control+B (Windows/Linux). You will be taken to basic.dartthe document . Since is Columnexpanded Flex, please navigate to the Flexsource code (also basic.dartin ).
  • Scroll down until you find a method createRenderObject()called . As you can see, this method returns a RenderFlex. This Columnis the render object for . Now navigate to the source code RenderFlexof the , which will take you to flex.dartthe file.
  • Scroll down until you find a method performLayout()named . Here's how to do the layout for the columns.

at last

Recently, I also sorted out a JavaScript and ES note, a total of 25 important knowledge points, and explained and analyzed each knowledge point. It can help you quickly master the relevant knowledge of JavaScript and ES, and improve work efficiency.



Friends in need, you can click the card below to receive and share for free

Guess you like

Origin blog.csdn.net/Android062005/article/details/129667316