本问如今是官方文档的一部分了
当Fluter初学者问你为何组件里的width:100
不是100像素的时候,默认的答案就是告诉他们把组件放进一个Center
里,对吧?html
不要这么干。git
若是你这么干了,他们会一次一次的问你为何FittedBox
有问题,为何Column
会overflow,又或者IntrinsicWidth
是作什么的。github
因此,一开始就告诉他们Flutter的布局和html有很大的不一样,他们极可能就是html的高手,而后让他们记住如下的规则:函数
不理解这个规则,Flutter的布局是无法弄清楚的。因此,我(做者)以为最好今早的学会它。布局
细节:学习
好比一个Column组件,已经设定了padding
值,如今要给它的两个子组件设定布局:字体
组件 -- 询问父组件约束是啥。
父组件 -- 你只能是90
到300
宽,30
到85
高。
组件 -- 嗯~~ 我还要5个单位的padding,那么个人子组件只能有最大290
的宽和75
的高。
组件 -- 嗨,第一个子组件你必须是0
~290
宽,0
~75
高。
第一个子组件 -- 我要290
宽,20
高。
组件 -- 嗯~~ , 既然我要把第二个子组件放在第一个的下面,这样就剩下55
的高度给第二个子组件了。
组件 -- 嗨,第二个子组件你必须是0
~290
宽,0
~55
高。
第二个子组件 -- 好的,我要140
宽和30
高。
组件 -- 很好,我会把第一个子组件放在x轴:5
,y轴:5
,第二个子组件x轴:80
,y轴:25
的位置。
组件 -- 嗨,父组件。个人size是300
宽,60
高。
Flutter布局引擎在上面规则的基础上还有一些其余的限制:flex
下面是一个互动示例。spa
原文提到了CodePen,也能够在如下两种方法里选一种。
Container(color: Colors.red);
屏幕是Container
的父组件,它会把红色的Container
严丝合缝的约束在整个的屏幕内部。设计
因此,Container
填满了整个屏幕,处处都是红色。
Container(color: Colors.red, width: 100, height: 100)
Container
想要宽100,高100,可是不行。屏幕会强制它填满屏幕。
因此Container
填满了屏幕。
屏幕强制Center
填满整个屏幕,因此Center
显示在全屏。
Center
告诉Container
能够拥有想要的大小,可是不能比屏幕还大。因此,Container
的大小就是100x100。
Align( alignment: Alignment.bottomRight, child: Container(width: 100, height: 100, color: Colors.red) )
这和前一个例子并不同,这里用的是Align
而不是Center
。
Align
也会告诉Container
能够任意大小,可是若是有任意的可用空间,是不让Container
居中的,它会把Contaienr
放在右下角。
Center( child: Container( color: Colors.red, width: double.infinity, height: double.infinity, ) )
屏幕强制Center
填充整个屏幕,因此Center
填满了屏幕。
Center
告诉Container
能够是任意大小,Container
要的是无限大,可是它又不能比屏幕还大,因此也填满了屏幕。
Center(child: Container(color: Colors.red))
屏幕仍是会强制Center
填充屏幕。
Center
会告诉Container
能够为任意大小,可是不能比屏幕大。由于Container
没有子组件,也没有固定的大小。它会决定显示为尽量的大,因此填充了屏幕。
可是,为何Container
要这么决定呢?这是设计决定的。因此,Container
在这样的状况下会如何显示,你要查看文档。
Center( child: Container( color: Colors.red, child: Container(color: Colors.green, width: 30, height: 30), ) )
Center
会填充屏幕。
Center
会告诉Container
能够任意大小。Container
没有大小,可是有一个子组件,因此它决定和它的子组件同样大小。
红色的Container
告诉它的子组件能够为任意大小,可是不能比屏幕还大。
绿色的Container
想要30 x 30。就像上面说的,红色的Container
就会显示为绿色的Container
的大小,也是30x30。没有红色能够显示出来,由于绿色的把红色所有覆盖住了。
Center( child: Container( color: Colors.red, padding: const EdgeInsets.all(20.0), child: Container(color: Colors.green, width: 30, height: 30), ) )
红色的Container
会显示为其子组件的大小,可是它本身还有padding因此它自己的大小是70x70(=30x30 + 20的padding值)。最后红色由于有padding值是可见的,绿色Container
和上例同样有30x30的大小。
ConstrainedBox( constraints: BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ), child: Container(color: Colors.red, width: 10, height: 10), )
你能够猜到Container
会在70到150的大小之间。可是,你看能会错。ConstrainedBox
只会添加父组件传递的约束以外的约束。
本例中,屏幕强制ConstrainedBox
为屏幕大小。因此它会告诉它的子组件Container
显示到屏幕的大小,因此constaints
参数的值都被忽略了。
Center( child: ConstrainedBox( constraints: BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ), child: Container(color: Colors.red, width: 10, height: 10), ) )
如今Center
会容许ConstrainedBox
是屏幕里的任意大小。ConstrainedBox
会让它的子组件使用额外的约束,并把这个约束做为constraints
参数传入子组件。
因此Container
必须在70到150之间,Container
虽然设定为10的大小,可是最后仍是显示为70(最小值)。
Center( child: ConstrainedBox( constraints: BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ), child: Container(color: Colors.red, width: 1000, height: 1000), ) )
Center
容许ConstraintedBox
是屏幕内的任意大小。ConstrainedBox
会把它的额外约束经过constraints
参数传入给它的子组件。
因此,Container
必须是在70到150之间。它想要设定为1000,因此最后的值为150(最大值)。
Center( child: ConstrainedBox( constraints: BoxConstraints( minWidth: 70, minHeight: 70, maxWidth: 150, maxHeight: 150, ), child: Container(color: Colors.red, width: 100, height: 100), ) )
Center
容许ConstrainedBox
拥有屏幕内的任意大小。ConstrainedBox
会对子组件施加额外的约束。
因此,Container
必须是70到150之间的值。它设定的值是100,因此它就会有这个值,由于它是在70到150之间的。
UnconstrainedBox( child: Container(color: Colors.red, width: 20, height: 50), )
屏幕强制UnconstrainedBox
拥有和屏幕同样的大小。而UnconstrainedBox
容许它的子组件有任意大小。
UnconstrainedBox( child: Container(color: Colors.red, width: 4000, height: 50), );
屏幕强制UnconstrainedBox
和屏幕同样大小,而UnconstrainedBox
让它的Container
子组件拥有任意大小。
可是,本例中Container
设定的是4000的宽,这样太大了无法放进UnconstrainedBox
,因此UnconstrainedBox
会显示出“overflow warning”。
OverflowBox( minWidth: 0.0, minHeight: 0.0, maxWidth: double.infinity, maxHeight: double.infinity, child: Container(color: Colors.red, width: 4000, height: 50), );
屏幕强制OverflowBox
和屏幕一个大小,而且OverflowBox
让它的子组件Container
能够有任意大小。
OverflowBox
和UnconstrainedBox
相似,不一样的地方是,若是子组件比它大的话不会包warning。
在本例中Container
的宽是4000,太大了。可是OverflowBox
在这里就不会像上例的UnconstrainedBox
同样报警。
UnconstrainedBox( child: Container( color: Colors.red, width: double.infinity, height: 100, ) )
它不会绘制出任何的东西,只会在console里报错。
UnconstrainedBox
让它的子组件能够拥有任意大小,然而它的子组件的宽是double.infinity
。
Flutter无法绘制无限宽的大小,因此它会抛出一个错误:BoxConstraints forces an infinite width。
UnconstrainedBox( child: LimitedBox( maxWidth: 100, child: Container( color: Colors.red, width: double.infinity, height: 100, ) ) )
FittedBox( child: Text('Some Example Text.'), )
屏幕强制FittedBox
和屏幕一样大小。Text
会有本身的宽度(也叫作intrinsic宽度)。这个值依赖于字体和文字的多少等。
FittedBox
会让Text
拥有任意的大小,可是Text
把它本身的大小通知FittedBox
以后,FittedBox
会作缩放,直到填满整个的宽度。
Center( child: FittedBox( child: Text('Some Example Text.'), ) )
可是,若是把FittedBox
放在Center
里面的话会发生什么呢?Center
会让FittedBox
拥有任意它想要的大小。
FittedBox
而后会把本身的大小缩放到Text
的大小。由于FittedBox
和Text
有一样的大小,因此就不会有缩放的发生了。
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.'), ) )
FittedBox
放在Center
里,并且文字内容多到屏幕放不下的时候会发生什么呢?
FittedBox
会缩放到适应Text
的大小,可是它不可能比屏幕还大。它会首先占用屏幕的大小,而后对Text
缩放到能够显示在屏幕里。
Center( child: Text('This is some very very very large text that is too big to fit a regular screen in a single line.'), )
若是去掉了FittedBox
, Text
就会使用屏幕的宽度,而后折行来适应这个宽度。
FittedBox( child: Container( height: 20.0, width: double.infinity, ) )
注意FittedBox
只能缩放一个有边界的组件(宽度或者高度都没有无限值)。不然它不会绘任何的东西,只会在console里显示一条报错信息。
Row( children:[ Container(color: Colors.red, child: Text('Hello!')), Container(color: Colors.green, child: Text('Goodbye!)), ] )
屏幕会强制Row
使用屏幕的宽度。
就和UnconstrainedBox
同样,Row
也不会对它的子组件施加任何的约束,而是容许他们有他们想要的任意大小。Row
会把他们挨个放好,而后空出剩余的空间。
Row( children:[ Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.')), Container(color: Colors.green, child: Text('Goodbye!')), ] )
Row
并不会对它的子组件施加额外的约束,它的子组件太大无法彻底适应Row
的宽度。这时候,就和UnconstrainedBox
同样显示错误信息“overflow warning”。
Row( children:[ Expanded( child: Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.')) ), Container(color: Colors.green, child: Text('Goodbye!')), ] )
若是一个Row
的子组件被Expanded
包裹的话,那么这个组件的宽度就不会再起做用了。它会使用Expanded
的宽度。Expanded
则强制原先的子组件使用Expanded
的宽度。
总之一句话,只要你用了Expanded
,那么它子组件的宽度就无效了。
Row( children:\[ Expanded( child: Container(color: Colors.red, child: Text(‘This is a very long text that won’t fit the line.’)), ), Expanded( child: Container(color: Colors.green, child: Text(‘Goodbye!’), ), ] )
若是一个Row
组件的全部子组件都由Expanded
包裹,那么每一个子组件所占的比例是由flex
参数决定的。每一个子组件租后都会接受Expanded
的宽度。
Row(children:[ Flexible( child: Container(color: Colors.red, child: Text('This is a very long text that won’t fit the line.'))), Flexible( child: Container(color: Colors.green, child: Text(‘Goodbye!’))), ] )
本例惟一不一样的就是使用Flexible
代替了Expanded
。Flexible
会让它的子组件有更小的宽度吗?
Flexible
和Expanded
都会忽略子组件的大小。
也就是说在Row
里不可能根据子组件的大小按比例缩放子组件。Row
要么使用子组件的宽,要么在你使用了Expanded
或者Flexible
的时候彻底忽略他们的宽度。
屏幕强制Scaffold
显示到屏幕的大小。因此,Scaffold
填充了屏幕。
Scaffold
告诉Container
它可使屏幕内的任意大小。
注意:当一个组件告诉它的子组件比某个特定的值小,咱们能够认为这个组件给它的子组件提供了“松散”的约束。后面会有更详细讲解。
Scaffold( body: **SizedBox.expand**( child: Container( color: blue, child: Column( children: \[ Text('Hello!'), Text('Goodbye!'), ], ))))
若是咱们要Scaffold
的子组件和它有相同的大小。咱们能够把它的子组件放到SizedBox.expand
。
注意:当一个组件告诉它的子组件必须是某个特定的值,咱们能够说这个组件给它的子组件提供了“紧”约束。
常常会听到一种说法“紧”或者“松散”的约束,那么他们到底指的是什么呢?
一个紧约束只提供了一种可能,一个准确的值。也就是说,一个紧约束,它的最大宽和最小宽是同样的,最大高和最小高也是同样的。
若是你到Flutter的box.dart
文件查找BoxConstraints
构造函数,你会发现:
BoxConstraints.tight(Size size) : minWidth = size.width, maxWidth = size.width, minHeight = size.height, maxHeight = size.height;
若是你再看示例 2,屏幕强制红色的Container
显示到屏幕的大小。屏幕能够这么作就是给Container
传入了紧约束。
一个松散的约束是指给子组件设定最大宽和高,可是让子组件尽量的小。也就是说,一个松散约束有最小的宽和高,他们都等于0.
BoxConstraints.loose(Size size) : minWidth = 0.0, maxWidth = size.width, minHeight = 0.0, maxHeight = size.height;
早示例 3中,Center
让红色的Container
不要比屏幕还大。Center
就是给Container
传入了一个松散的约束。最后的效果是Center
收到了父组件传给它的紧约束,而给它的子组件Container
传入了松散约束。
知道了基本的布局规则,也仍是不够的。
每一个组件都有高度的自由决定如何使用基本布局规则,因此若是只是看到了组件名称是没法预测这个组件的布局行为的。
若是你去猜,基本都会错。你不读文档或者组件的源代码是没法准确知道一个组件如何布局的。
源代码基本都很复杂,因此最好仍是看文档。固然若是你想学习布局的相关代码,IDE的支持是足够支持你的想法的。
这里有个例子:
Column
,而后跳转到它的源码。最终你会到basic.dart
文件。Column
继承自Flex
,还能够跳转到Flex
,它也在basic.dart
文件里。createRenderObject
的方法。这个方法返回RenderFlex
。这就是Column
对应的绘制对象。再跳转到RenderFlex
的源码,你就会到flex.dart
文件。的方法,这个方法就是为
Column`布局的方法。很是感谢Simon Lightfoot的校验,文章开头的图片和对文章内容的建议。