共计 5194 个字符,预计需要花费 13 分钟才能阅读完成。
本问现在是官方文档的一部分了
当 Fluter 初学者问你为什么组件里的 width:100
不是 100 像素的时候,默认的答案就是告诉他们把组件放进一个 Center
里,对吧?
不要这么干。
如果你这么干了,他们会一次一次的问你为什么 FittedBox
有问题,为什么 Column
会 overflow,又或者 IntrinsicWidth
是做什么的。
所以,一开始就告诉他们 Flutter 的布局和 html 有很大的不同,他们很可能就是 html 的高手,然后让他们记住以下的规则:
? 约束(Constraint)向下,大小(Size)向上,位置父决定
不理解这个规则,Flutter 的布局是没法弄清楚的。所以,我(作者)觉得最好今早的学会它。
细节:
- 一个组件都是从它的 父组件 获得 约束(constraint)。一个约束就是四个 double 值:一个最小、最大宽度和一个最小、最大高度。
- 然后,这个组件遍历它的 子组件 。一个个的通知它的子组件他们的 约束(每个子组件都可能不一样),然后询问他们想要的 size。
- 然后,这个组件沿着横向的 x 轴和纵向的 y 轴排列它的子组件的 位置。
- 最后,每个组件告诉它的父组件它自己在约束下的size。
比如一个 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
高。
限制(Limitation)
Flutter 布局引擎在上面规则的基础上还有一些其他的限制:
- 一个组件只可以在父组件传过来的约束的范围内确定它的大小(size)。也就是说,一般一个组件 不能想多大就多大。
- 一个组件 不知道,也不能决定它在屏幕上的位置。组件的位置是由它的父组件决定的。
- 父组件的大小和位置也是由它的父组件决定的,只有在 树的概念下才能决定一个组件的大小和位置。
示例
下面是一个互动示例。
原文提到了 CodePen,也可以在以下两种方法里选一种。
- 使用 DartPad.
- 代码的 github repo
示例 1
Container(color: Colors.red);
屏幕是 Container
的父组件,它会把红色的 Container
严丝合缝的约束在整个的屏幕内部。
所以,Container
填满了整个屏幕,到处都是红色。
示例 2
Container(color: Colors.red, width: 100, height: 100)
Container
想要宽 100,高 100,但是不行。屏幕会强制它填满屏幕。
所以 Container
填满了屏幕。
示例 3
屏幕强制 Center
填满整个屏幕,所以 Center
显示在全屏。
Center
告诉 Container
可以拥有想要的大小,但是不能比屏幕还大。所以,Container
的大小就是 100×100。
示例 4
Align(
alignment: Alignment.bottomRight,
child: Container(width: 100, height: 100, color: Colors.red)
)
这和前一个例子并不一样,这里用的是 Align
而不是Center
。
Align
也会告诉 Container
可以任意大小,但是如果有任意的可用空间,是不让 Container
居中的,它会把 Contaienr
放在右下角。
示例 5
Center(
child: Container(
color: Colors.red,
width: double.infinity,
height: double.infinity,
)
)
屏幕强制 Center
填充整个屏幕,所以 Center
填满了屏幕。
Center
告诉 Container
可以是任意大小,Container
要的是无限大,但是它又不能比屏幕还大,所以也填满了屏幕。
示例 6
Center(child: Container(color: Colors.red))
屏幕还是会强制 Center
填充屏幕。
Center
会告诉 Container
可以为任意大小,但是不能比屏幕大。因为 Container
没有子组件,也没有固定的大小。它会决定显示为尽可能的大,所以填充了屏幕。
但是,为什么 Container
要这么决定呢?这是设计决定的。所以,Container
在这样的情况下会如何显示,你要查看文档。
示例 7
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
的大小,也是 30×30。没有红色可以显示出来,因为绿色的把红色全部覆盖住了。
示例 8
Center(
child: Container(
color: Colors.red,
padding: const EdgeInsets.all(20.0),
child: Container(color: Colors.green, width: 30, height: 30),
)
)
红色的 Container
会显示为其子组件的大小,但是它自己还有 padding 所以它本身的大小是 70×70(=30×30 + 20 的 padding 值)。最后红色因为有 padding 值是可见的,绿色 Container
和上例一样有 30×30 的大小。
示例 9
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
参数的值都被忽略了。
示例 10
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(最小值)。
示例 11
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(最大值)。
示例 12
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 之间的。
示例 13
UnconstrainedBox(child: Container(color: Colors.red, width: 20, height: 50),
)
屏幕强制 UnconstrainedBox
拥有和屏幕一样的大小。而 UnconstrainedBox
允许它的子组件有任意大小。
示例 14
UnconstrainedBox(child: Container(color: Colors.red, width: 4000, height: 50),
);
屏幕强制 UnconstrainedBox
和屏幕一样大小,而 UnconstrainedBox
让它的 Container
子组件拥有任意大小。
但是,本例中 Container
设定的是 4000 的宽,这样太大了没法放进 UnconstrainedBox
,所以UnconstrainedBox
会显示出“overflow warning”。
示例 15
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
一样报警。
示例 16
UnconstrainedBox(
child: Container(
color: Colors.red,
width: double.infinity,
height: 100,
)
)
它不会绘制出任何的东西,只会在 console 里报错。
UnconstrainedBox
让它的子组件可以拥有任意大小,然而它的子组件的宽是double.infinity
。
Flutter 没法绘制无限宽的大小,所以它会抛出一个错误:BoxConstraints forces an infinite width。
示例 17
UnconstrainedBox(
child: LimitedBox(
maxWidth: 100,
child: Container(
color: Colors.red,
width: double.infinity,
height: 100,
)
)
)
示例 18
FittedBox(child: Text('Some Example Text.'),
)
屏幕强制 FittedBox
和屏幕同样大小。Text
会有自己的宽度(也叫做 intrinsic 宽度)。这个值依赖于字体和文字的多少等。
FittedBox
会让 Text
拥有任意的大小,但是 Text
把它自己的大小通知 FittedBox
之后,FittedBox
会做缩放,直到填满整个的宽度。
示例 19
Center(
child: FittedBox(child: Text('Some Example Text.'),
)
)
但是,如果把 FittedBox
放在 Center
里面的话会发生什么呢?Center
会让 FittedBox
拥有任意它想要的大小。
FittedBox
然后会把自己的大小缩放到 Text
的大小。因为 FittedBox
和Text
有同样的大小,所以就不会有缩放的发生了。
示例 20