布局约束规则
首先,上层 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
)转换为宽松约束。