Flutter 必须知道的布局规则
Posted 会煮咖啡的猫咪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 必须知道的布局规则相关的知识,希望对你有一定的参考价值。
Flutter 必须知道的布局规则
很多人说布局不熟练,我建议是先去了解布局规则,我之前的文章有讲过。
今天看到一文写的很好,我做了翻译,时间关系没有润色太多,然后我也准备了视频做了解说,希望能帮到你。
原文 https://rohanjariwala03.medium.com/flutter-the-advanced-layout-rule-even-beginners-must-know-87eebb54cef6
前言
当学习 Flutter 的人问你为什么某个 widget 有宽度: 100 不是 100 像素宽,默认的答案是告诉他们把这个 widget 放在一个 center ,对不对?
别这样。
如果你这样做了,他们会一次又一次地问你为什么有些 FittedBox 不能工作,为什么那个 Column 会溢出,或者 IntrinsicWidth 应该做什么。
相反,首先告诉他们 Flutter 的布局与 html 的布局非常不同(这可能是他们来自哪里) ,然后让他们记住以下规则:
约束向下传递,尺寸向上传递,位置由父组件决定。
如果不知道这个规则,就不能真正理解 Flutter 布局,所以我相信每个人都应该尽早学习它。
细节:
-
widget 从其父部件获得自己的约束。“约束”只是一组 4 个双精度: 最小和最大宽度,以及最小和最大高度。 -
然后, widget 遍历它自己的子列表。 widget 一个接一个地告诉它的子元素它们的约束条件是什么(每个子元素的约束条件可能不同) ,然后询问每个子元素它希望是什么大小。 -
然后, widget 将其子元素(水平放在 x 轴上,垂直放在 y 轴上)一个接一个地放置。 -
最后, widget 告诉它的父部件它自己的大小(当然是在原始约束内)。
例如,如果一个 widget 像一个带有一些填充的列,并且想要布局它的两个子元素:
widget - 嘿, parent ,我的约束条件是什么?
父级 - 像素宽度必须在 90 到 300 之间,身高必须在 30 到 85 之间。
Widget ー嗯,因为我想要有 5 个像素的填充,那么我的 child 最多可以有 290 个像素的宽度和 75 个像素的高度。
widget - 嘿,第一个 child ,你必须从 0 到 290 像素宽,0 到 75 高。
第一个 child - 好的,那么我希望是 290 像素宽,20 像素高。
Widget ー 嗯,因为我想把我的第二个 child 放在第一个 child 的下面,所以我的第二个 child 只有 55 像素的高度。
widget - 嘿,第二个 child ,你必须从 0 到 290 宽,0 到 55 高。
第二个 child - 好的,我希望是 140 像素宽,30 像素高。
widget ー 很好。我把第一个 child 放在 x: 5 和 y: 5 的位置,第二个 child 放在 x: 80 和 y: 25 的位置。
widget - 嘿, parent ,我已经决定我的尺寸是 300 像素宽,60 像素高。
Limitations 限制
由于上面描述的布局规则,Flutter 的布局引擎有一些重要的局限性:
-
widget 只能在其父节点给它的约束范围内决定自己的大小。这意味着一个 widget 通常不能有它想要的任何大小。 -
一个 widget 不能知道也不能决定它自己在屏幕上的位置,因为决定 widget 位置的是 widget 的父部件。 -
由于父节点的大小和位置依次取决于它自己的父节点,因此如果不考虑整个树,就不可能精确地定义任何 widget 的大小和位置。
代码
https://github.com/ducafecat/flutter_layout_article
例子 1
Container(color: Colors.red)
屏幕是 Container 的父级。它强制红色 container 与屏幕大小完全相同。
所以 container 填满了屏幕,变成了红色。
例子 2
Container(width: 100, height: 100, color: Colors.red)
红色的 container 想要 100 × 100,但是它不能,因为屏幕强迫它和屏幕尺寸完全一样。
所以 container 填满了屏幕。
例子 3
Center(
child: Container(width: 100, height: 100, color: Colors.red)
)
屏幕强制 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,而是将其对齐到可用空间的右下角。
例子 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 填充了整个屏幕。
center 告诉 container 它是任何大小,它想要的,但不超过屏幕。因为 Container 没有子元素,也没有固定的大小,所以它决定要尽可能地大,因此它适合整个屏幕。
但是为什么 container 决定这样做呢?仅仅是因为这是那些创建 Container widget 的人的设计决策。它可以以不同的方式创建,实际上您必须阅读 Container 的文档,以了解它将根据具体情况做什么。
例子 7
Center(
child: Container(
color: Colors.red,
child: Container(color: Colors.green, width: 30, height: 30),
)
)
屏幕强制 center 与屏幕大小完全相同,因此 center 填充了整个屏幕。
center 告诉红色 container 它可以是任意大小,但不能大于屏幕。由于红色 container 没有大小,但是有一个子 container ,所以它决定希望其子 container 的大小相同。
红色 container 告诉它的子 container 可以是任意大小,但不能大于屏幕。
child 刚好是一个绿色的 container ,要 30 × 30。如上所述,红色的 container 将自己大小的子大小,所以它也将是 30 × 30。不可见红色,因为绿色 container 将占据所有红色 container 。
例子 8
Center(
child: Container(
color: Colors.red,
padding: const EdgeInsets.all(20.0),
child: Container(color: Colors.green, width: 30, height: 30),
)
)
红色 container 将根据其子 container 大小调整自身大小,但是它会考虑自己的填充。因此,它将是 70 × 70(= 30 × 30 加上 20 像素的填充所有方面)。由于填充,红色将可见,绿色 container 的大小将与前面的示例相同。
例子 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 也假定屏幕大小,从而忽略其约束参数。
例子 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 将把其约束参数中的附加约束强加给它的子约束。
所以 container 必须在 70 到 150 像素之间。它希望有 10 个像素,所以它最终将有 70(最低)。
例子 11
Center( center (
child: ConstrainedBox( Child: ConstrainedBox (
constraints: BoxConstraints( 限制条件:
minWidth: 70,最小宽度: 70,
minHeight: 70, 身高: 70,
maxWidth: 150,最大宽度: 150,
maxHeight: 150, 身高: 150,
),
child: Container(color: Colors.red, width: 1000, height: 1000), child: Container (颜色: Colors.red,宽度: 1000,高度: 1000) ,
)
)
center 将允许 ConstrainedBox 的大小不超过屏幕的大小。ConstrainedBox 将把其约束参数中的附加约束强加给它的子约束。
所以 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),
)
屏幕强制 UnstrainedBox 与屏幕大小完全相同。但是,UnstrainedBox 允许其 Container 子元素具有任意大小。
例子 14
UnconstrainedBox(
child: Container(color: Colors.red, width: 4000, height: 50),
);
屏幕强制 UnstrainedBox 的大小与屏幕的大小完全相同,并且 UnstrainedBox 允许其 Container 子节点拥有任何它想要的大小。
不幸的是,在这种情况下,Container 有 4000 像素的宽度,太大以至于无法放入 UnstrainedBox,所以 UnstrainedBox 将显示令人恐惧的“溢出警告”。
例子 15
OverflowBox(
minWidth: 0.0,
minHeight: 0.0,
maxWidth: double.infinity,
maxHeight: double.infinity,
child: Container(color: Colors.red, width: 4000, height: 50),
);
该屏幕强制 Overflow Box 与屏幕的大小完全相同,并且 Overflow Box 允许其 Container 子节点拥有任何它想要的大小。
像这样使用 Overflow Box 类似于 UnstrainedBox,不同之处在于,如果子元素不适合这个空间,它将不会显示任何警告。
在这种情况下, container 有 4000 像素的宽度,太大以至于无法放入 Overflow Box,但是 Overflow Box 将简单地显示它所能显示的内容,没有给出任何警告。
例子 16
UnconstrainedBox(
child: Container(
color: Colors.red,
width: double.infinity,
height: 100,
)
)
这不会呈现任何内容,您将在控制台中得到一个错误。
UnstrainedBox 允许其子元素具有任意大小,但是其子元素是具有无限大小的 Container。
Flutter 不能呈现无限大小,因此它会抛出一个错误消息: BoxConstraint 强制使用无限宽度。
例子 17
UnconstrainedBox(
child: LimitedBox(
maxWidth: 100,
child: Container(
color: Colors.red,
width: double.infinity,
height: 100,
)
)
)
这里不会再出现错误,因为当 LimitedBox 被 UnstrainedBox 赋予无限大小时,它将向其子级传递最大宽度为 100 的值。
注意,如果将 UnstrainedBox 更改为 Center widget ,LimitedBox 将不再应用它的限制(因为它的限制只在获得无限限制时应用) ,并且 container 宽度将允许增长超过 100。
这清楚地说明了 LimitedBox 和 ConstrainedBox 之间的区别。
例子 18
FittedBox(
child: Text('Some Example Text.'),
)
屏幕强制 FittedBox 与屏幕大小完全相同。文本将有一些自然宽度(也称为其固有宽度) ,这取决于文本的数量,其字体大小等。
FittedBox 将允许 Text 拥有它想要的任何大小,但是在 Text 告诉 FittedBox 它的大小之后,FittedBox 将缩放它,直到它填满所有可用的宽度。
例子 19
Center(
child: FittedBox(
child: Text('Some Example Text.'),
)
)
但是如果我们把 FittedBox 放到 Center 中会发生什么呢?Center 将允许 FittedBox 拥有它想要的任何大小,直到屏幕大小为止。
然后 FittedBox 将自己调整到 Text 的大小,并让 Text 拥有它想要的任何大小。由于 FittedBox 和 Text 具有相同的大小,因此不会发生缩放。
例子 20
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 内部,而 Text 太大而不能适应屏幕,会发生什么情况?
FittedBox 将尝试根据文本调整自身大小,但它不能大于屏幕。然后,它将假设屏幕大小,并调整文本的大小,使其适合屏幕。
例子 21
如何更轻松地实现一个 Flutter 布局?