布局约束规则
首先,上层 widget 向下层 widget 传递约束条件,然后,下层 widget 向上层 widget 传递大小信息,最后,上层 widget 决定下层 widget 的位置。
-
一个
widget仅在其父级给其约束的情况下才能决定自身的大小。这意味着widget通常情况下 不能任意获得其想要的大小。 -
一个
widget无法知道,也不需要决定其在屏幕中的位置。因为它的位置是由其父级决定的。 -
当轮到父级决定其大小和位置的时候,同样的也取决于它自身的父级。所以,在不考虑整棵树的情况下,几乎不可能精确定义任何
widget的大小和位置。 -
如果子级想要拥有和父级不同的大小,然而父级没有足够的空间对其进行布局的话,子级的设置的大小可能会不生效。 这时请明确指定它的对齐方式。
实例1
import 'package:flutter/material.dart';
class LayoutPage extends StatelessWidget {
const LayoutPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
整个屏幕作为 Container 的父级,并且强制 Container 变成和屏幕一样的大小。
所以这个 Container 充满了整个屏幕,并绘制成红色。
实例2
return Container(
color: Colors.red,
width: 100,
height: 100,
);
红色的 Container 想要变成 100 x 100 的大小,但是它无法变成,因为屏幕强制它变成和屏幕一样的大小。所以 Container 充满了整个屏幕。

实例3
return Center(
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
);
屏幕强制 Center 变得和屏幕一样大,所以 Center 充满了屏幕。
然后 Center 告诉 Container 可以变成任意大小,但是不能超出屏幕。现在,Container 可以真正变成 100 × 100 大小了。

实例4
return Align(
// alignment: Alignment.bottomRight,
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
);
与上一个样例不同的是,我们使用了 Align 而不是 Center。Align 同样也告诉 Container,你可以变成任意大小。Align默认居中。
实例5
return Center(
child: Container(
color: Colors.red,
width: double.infinity,
height: double.infinity,
),
);
屏幕强制 Center 变得和屏幕一样大,所以 Center 充满屏幕。
然后 Center 告诉 Container 可以变成任意大小,但是不能超出屏幕。现在,Container 想要无限的大小,但是由于它不能比屏幕更大,所以就仅充满屏幕。
实例6
return Center(
child: Container(
color: Colors.red,
child: Container(
color: Colors.green,
width: 50,
height: 50,
),
),
);
屏幕强制 Center 变得和屏幕一样大,所以 Center 充满屏幕。
然后 Center 告诉红色的 Container 可以变成任意大小,但是不能超出屏幕。由于 Container 没有固定大小但是有子级,所以它决定变成它 child 的大小。
然后红色的 Container 告诉它的 child 可以变成任意大小,但是不能超出屏幕。
而它的 child 是一个想要 50 × 50 大小绿色的 Container。由于红色的 Container 和其子级一样大,所以也变为 50 × 50。由于绿色的 Container 完全覆盖了红色 Container,所以你看不见它了。

实例7
return Center(
child: Container(
color: Colors.red,
padding: const EdgeInsets.all(20.0),
child: Container(
color: Colors.green,
width: 50,
height: 50,
),
),
);
红色 Container 变为其子级的大小,但是它将其 padding 带入了约束的计算中。所以它有一个 20 x 20 的外边距。由于这个外边距,所以现在你能看见红色了。而绿色的 Container 则还是和之前一样。

实例8 (ConstrainedBox)
return ConstrainedBox(
constraints: const BoxConstraints(
minHeight: 70,
minWidth: 70,
maxHeight: 150,
maxWidth: 150,
),
child: Container(
color: Colors.red,
width: 20,
height: 20,
),
);
你可能会猜想 Container 的尺寸会在 70 到 150 像素之间,但并不是这样。 ConstrainedBox 仅对其从其父级接收到的约束下施加其他约束。
在这里,屏幕迫使 ConstrainedBox 与屏幕大小完全相同,因此它告诉其子 Widget 也以屏幕大小作为约束,从而忽略了其 constraints 参数带来的影响。

实例9 (ConstrainedBox)
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
minHeight: 70,
minWidth: 70,
maxHeight: 150,
maxWidth: 150,
),
child: Container(
color: Colors.red,
width: 20,
height: 20,
),
),
);
现在,Center 允许 ConstrainedBox 达到屏幕可允许的任意大小。 ConstrainedBox 将 constraints 参数带来的约束附加到其子对象上。
Container 必须介于 70 到 150 像素之间。虽然它希望自己有 20 个像素大小,但最终获得了 70 个像素(最小为 70)。

实例10 (UnconstrainedBox)
return UnconstrainedBox(
child: Container(
color: Colors.red,
width: 30,
height: 50,
),
);
屏幕强制 UnconstrainedBox 变得和屏幕一样大,而 UnconstrainedBox 允许其子级的 Container 可以变为任意大小。

实例11 (UnconstrainedBox)
return UnconstrainedBox(
child: Container(
color: Colors.red,
width: 3000,
height: 50,
),
);
屏幕强制 UnconstrainedBox 变得和屏幕一样大,而 UnconstrainedBox 允许其子级的 Container 可以变为任意大小。
不幸的是,在这种情况下,容器的宽度为 3000 像素,这实在是太大,以至于无法容纳在 UnconstrainedBox 中,因此 UnconstrainedBox 将显示溢出警告(overflow warning)。

实例12 (UnconstrainedBox)
return UnconstrainedBox(
child: Container(
color: Colors.red,
width: double.infinity,
height: 50,
),
);
这将不会渲染任何东西,而且你能在控制台看到错误信息。
UnconstrainedBox 让它的子级决定成为任何大小,但是其子级是一个具有无限大小的 Container。
Flutter 无法渲染无限大的东西,所以它抛出以下错误: BoxConstraints forces an infinite width.(盒子约束强制使用了无限的宽度)

实例13 (UnconstrainedBox和LimitedBox)
return UnconstrainedBox(
child: LimitedBox(
maxWidth: 100,
child: Container(
color: Colors.red,
width: double.infinity,
height: 50,
),
),
);
这次你就不会遇到报错了。 UnconstrainedBox 给 LimitedBox 一个无限的大小;但它向其子级传递了最大为 100 的约束。
如果你将 UnconstrainedBox 替换为 Center,则LimitedBox 将不再应用其限制(因为其限制仅在获得无限约束时才适用),并且容器的宽度允许超过 100。
上面的样例解释了 LimitedBox 和 ConstrainedBox 之间的区别。

实例14 (OverflowBox)
return OverflowBox(
minHeight: 0.0,
minWidth: 0.0,
maxHeight: double.infinity,
maxWidth: double.infinity,
child: Container(
color: Colors.red,
width: 3000,
height: 50,
),
);
屏幕强制 OverflowBox 变得和屏幕一样大,并且 OverflowBox 允许其子容器设置为任意大小。
OverflowBox 与 UnconstrainedBox 类似,但不同的是,如果其子级超出该空间,它将不会显示任何警告。
在这种情况下,容器的宽度为 3000 像素,并且太大而无法容纳在 OverflowBox 中,但是 OverflowBox 会全部显示,而不会发出警告。

实例15 (FittedBox)
return const FittedBox(
child: Text('Some Example Text.'),
);
屏幕强制 FittedBox 变得和屏幕一样大,而 Text 则是有一个自然宽度(也被称作 intrinsic 宽度),它取决于文本数量,字体大小等因素。
FittedBox 让 Text 可以变为任意大小。但是在 Text 告诉 FittedBox 其大小后, FittedBox 缩放文本直到填满所有可用宽度。

严格约束(Tight) vs 宽松约束(loose)
- 严格约束(Tight BoxConstraints 构造器:
BoxConstraints.tight(Size size)
: minWidth = size.width,
maxWidth = size.width,
minHeight = size.height,
maxHeight = size.height;
实例2告诉我们屏幕强制 Container 变得和屏幕一样大。为何屏幕能够做到这一点,原因就是给 Container 传递了严格约束。
- 宽松约束(loose)
一个 宽松 约束,换句话来说就是设置了最大宽度/高度,但是让允许其子
widget获得比它更小的任意大小。换句话来说,宽松约束的最小宽度/高度为 0。
BoxConstraints.loose(Size size)
: minWidth = 0.0,
maxWidth = size.width,
minHeight = 0.0,
maxHeight = size.height;
Center 让红色的 Container 变得更小,但是不能超出屏幕。Center 能够做到这一点的原因就在于给 Container 的是一个宽松约束。总的来说,Center 起的作用就是从其父级(屏幕)那里获得的严格约束,为其子级(Container)转换为宽松约束。