布局组件分类
1.ConstrainedBox
ConstrainedBox
用于对子组件添加额外的约束。如果想让子组件的最小高度是80像素,你可以使用const BoxConstraints(minHeight: 80.0)
作为子组件的约束
ConstrainedBox(
constraints: BoxConstraints(
minWidth: double.infinity, //宽度尽可能大
minHeight: 50.0 //最小高度为50像素
),
child: Container(
height: 5.0,
child: redBox ,
),
)
可以看到,我们虽然将Container的高度设置为5像素,但是最终却是50像素,这正是ConstrainedBox的最小高度限制生效了。如果将Container的高度设置为80像素,那么最终红色区域的高度也会是80像素,因为在此示例中,ConstrainedBox只限制了最小高度,并未限制最大高度
2. SizedBox
SizedBox
用于给子元素指定固定的宽高 如:
SizedBox(
width: 80.0,
height: 80.0,
child: redBox
)
实际上SizedBox
只是ConstrainedBox
的一个定制,上面代码等价于:
ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 80.0,height: 80.0),
child: redBox,
)
而BoxConstraints.tightFor(width: 80.0,height: 80.0)
等价于:
BoxConstraints(minHeight: 80.0,maxHeight: 80.0,minWidth: 80.0,maxWidth: 80.0)
2. 多重限制
如果某一个组件有多个父级ConstrainedBox
限制,那么最终会是哪个生效?
ConstrainedBox(
constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0), //父
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子
child: redBox,
),
)
我们有父子两个ConstrainedBox
,他们的约束条件不同 效果如图:
最终显示效果是宽90,高60,也就是说是子ConstrainedBox
的minWidth
生效,而minHeight
是父ConstrainedBox
生效。
再看下面例子:
ConstrainedBox(
constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 60.0, minHeight: 60.0),
child: redBox,
)
)
最终的显示效果仍然是90,高60,效果相同,但意义不同,因为此时minWidth
生效的是父ConstrainedBox
,而minHeight
是子ConstrainedBox
生效。
通过上面示例,我们发现有多重限制时,对于minWidth
和minHeight
来说,是取父子中相应数值较大的。实际上,只有这样才能保证父限制与子限制不冲突。
3. UnconstrainedBox
虽然任何时候子组件都必须遵守其父组件的约束,但前提条件是它们必须是父子关系,假如有一个组件 A,它的子组件是B,B 的子组件是 C,则 C 必须遵守 B 的约束,同时 B 必须遵守 A 的约束,但是 A 的约束不会直接约束到 C,除非B将A对它自己的约束透传给了C。 利用这个原理,就可以实现一个这样的 B 组件:
- B 组件中在布局 C 时不约束C(可以为无限大)。
- C 根据自身真实的空间占用来确定自身的大小。
- B 在遵守 A 的约束前提下结合子组件的大小确定自身大小。
而这个 B组件就是 UnconstrainedBox
组件,也就是说UnconstrainedBox
的子组件将不再受到约束,大小完全取决于自己。一般情况下,我们会很少直接使用此组件,但在”去除”多重限制的时候也许会有帮助,我们看下下面的代码:
ConstrainedBox(
constraints: BoxConstraints(minWidth: 60.0, minHeight: 100.0), //父
child: UnconstrainedBox( //“去除”父级限制
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子
child: redBox,
),
)
)
上面代码中,如果没有中间的UnconstrainedBox
,那么根据上面所述的多重限制规则,那么最终将显示一个90×100的红色框。但是由于UnconstrainedBox
“去除”了父ConstrainedBox
的限制,则最终会按照子ConstrainedBox
的限制来绘制redBox
,即90×20,如图
但是,读者请注意,UnconstrainedBox
对父组件限制的“去除”并非是真正的去除:上面例子中虽然红色区域大小是90×20,但上方仍然有80的空白空间。也就是说父限制的minHeight
(100.0)仍然是生效的,只不过它不影响最终子元素redBox
的大小,但仍然还是占有相应的空间,可以认为此时的父ConstrainedBox
是作用于子UnconstrainedBox
上,而redBox
只受子ConstrainedBox
限制,这一点请读者务必注意。
那么有什么方法可以彻底去除父ConstrainedBox
的限制吗?答案是否定的!请牢记,任何时候子组件都必须遵守其父组件的约束,所以在此提示读者,在定义一个通用的组件时,如果要对子组件指定约束,那么一定要注意,因为一旦指定约束条件,子组件自身就不能违反约束。
在实际开发中,当我们发现已经使用 SizedBox
或 ConstrainedBox
给子元素指定了固定宽高,但是仍然没有效果时,几乎可以断定:已经有父组件指定了约束!举个例子,如 Material 组件库中的AppBar
(导航栏)的右侧菜单中,我们使用SizedBox
指定了 loading 按钮的大小,代码如下:
AppBar(
title: Text(title),
actions: <Widget>[
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(Colors.white70),
),
)
],
)
效果如图
我们会发现右侧loading按钮大小并没有发生变化!这正是因为AppBar
中已经指定了actions
按钮的约束条件,所以我们要自定义loading按钮大小,就必须通过UnconstrainedBox
来 “去除” 父元素的限制,代码如下:
AppBar(
title: Text(title),
actions: <Widget>[
UnconstrainedBox(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(Colors.white70),
),
),
)
],
)
运行后效果如图
生效了!实际上将 UnconstrainedBox 换成 Center 或者 Align 也是可以的,至于为什么,我们会在本书后面布局原理相关的章节中解释。
另外,需要注意,UnconstrainedBox 虽然在其子组件布局时可以取消约束(子组件可以为无限大),但是 UnconstrainedBox 自身是受其父组件约束的,所以当 UnconstrainedBox 随着其子组件变大后,如果UnconstrainedBox 的大小超过它父组件约束时,也会导致溢出报错,比如:
Column(
children: <Widget>[
UnconstrainedBox(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(children: [Text('xx' * 30)]),
),
),
]
运行效果如图