Flutter 布局约束

布局约束

Flutter 中有两种布局模型:

  • 基于 RenderBox 的盒模型布局。
  • 基于 Sliver ( RenderSliver ) 按需加载列表布局。

大致布局流程如下:

  1. 上层组件向下层组件传递约束(constraints)条件。
  2. 下层组件确定自己的大小,然后告诉上层组件。注意下层组件的大小必须符合父组件的约束。
  3. 上层组件确定下层组件相对于自身的偏移和确定自身的大小(大多数情况下会根据子组件的大小来确定自身的大小)。

因为任何时候子组件都必须先遵守父组件的约束

盒模型布局组件有两个特点:

  1. 组件对应的渲染对象都继承自 RenderBox 类。在本书后面文章中如果提到某个组件是 RenderBox,则指它是基于盒模型布局的,而不是说组件是 RenderBox 类的实例。
  2. 在布局过程中父级传递给子级的约束信息由 BoxConstraints 描述。

BoxConstraints (父->子)的约束信息

1
2
3
4
5
6
const BoxConstraints({
this.minWidth = 0.0, //最小宽度
this.maxWidth = double.infinity, //最大宽度
this.minHeight = 0.0, //最小高度
this.maxHeight = double.infinity //最大高度
})

BoxConstraints.tight(Size size),它可以生成固定宽高的限制;
BoxConstraints.expand()可以生成一个尽可能大的用以填充另一个容器的BoxConstraints;

ConstrainedBox

通过约束限制子组件大小的组件

1
2
3
4
// 临时定义一个
Widget yellowBox = const DecoratedBox(
decoration: BoxDecoration(color: Colors.yellow),
);
1
2
3
4
5
6
7
8
9
10
11
12
Widget testConstrainedBox() {
return ConstrainedBox( // 父组件
constraints: const BoxConstraints( // 父组件对子组件的 约束信息
minWidth: double.infinity, // 宽度尽可能大
minHeight: 80.0 // 最小高度为80像素
),
child: SizedBox( // 子组件
height: 20.0, // 虽然20小于80, 但是父组件约束了最小为80
child: yellowBox,
),
);
}

效果

虽然自组件设置的高是20, 但是父组件约束了最小为80, 所以高度是80

SizedBox

SizedBox只是ConstrainedBox的一个定制,

1
2
3
4
5
6
7
8
9
10
11
SizedBox(width: 80.0, height: 80.0, child: yellowBox)
// 等价于
ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 80.0, height: 80.0),
child: yellowBox,
)
// 又等价于
ConstrainedBox(
constraints: BoxConstraints(minHeight: 80.0, maxHeight: 80.0, minWidth: 80.0, maxWidth: 80.0),
child: yellowBox,
)

其实ConstrainedBoxSizedBox都是通过RenderConstrainedBox来渲染的;

多层约束

如果使用多层ConstrainedBox父组件 会是什么情况呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 外小 内大
ConstrainedBox( // 祖父组件
constraints: BoxConstraints(minWidth: 140.0, minHeight: 40.0),
child: ConstrainedBox( // 父组件
constraints: BoxConstraints(minWidth: 180.0, minHeight: 80.0),
child: yellowBox, // 子组件
),
);
// 外大 内小
ConstrainedBox( // 祖父组件
constraints: BoxConstraints(minWidth: 180.0, minHeight: 80.0),
child: ConstrainedBox( // 父组件
constraints: BoxConstraints(minWidth: 140.0, minHeight: 40.0),
child: yellowBox, // 子组件
),
);

因为约束条件是用来约束子组件的

  • 一层约束时很好理解, 即: maxSize >= 最终大小 && 最终大小 >= minSize
  • 那两层也不难理解, 只要两层约束条件都满足即可:
    1
    2
    3
    4
    5
    // maxSize1 >= 最终大小 && 最终大小 >= minSize1
    // &&
    // maxSize2 >= 最终大小 && 最终大小 >= minSize2
    // 整理即可得出
    // min(maxSize1, maxSize2) >= 最终大小 >= max(minSize1, minSize2)

UnconstrainedBox

虽然任何时候子组件都必须遵守其父组件的约束,但前提条件是它们必须是父子关系,

1
2
3
4
5
6
7
8
9
ConstrainedBox( // 曾祖父组件
constraints: BoxConstraints(minWidth: 180.0, minHeight: 80.0), // 曾祖父组件约束
child: UnconstrainedBox( // 祖父组件, “去除”曾祖父组件的约束
child: ConstrainedBox( // 父组件
constraints: BoxConstraints(minWidth: 80.0, minHeight: 30.0), // 父组件约束
child: yellowBox, // 子组件
),
),
);

任何时候子组件都必须遵守其父组件的约束

需要注意,UnconstrainedBox 虽然在其子组件布局时可以取消约束(子组件可以为无限大),但是 UnconstrainedBox 自身是受其父组件约束的,所以当 UnconstrainedBox 随着其子组件变大后,如果UnconstrainedBox 的大小超过它父组件约束时,也会导致溢出报错!

其他约束类

还有一些其他的尺寸限制类容器

  • AspectRatio 它可以指定子组件的长宽比、
  • LimitedBox 用于指定最大宽高、
  • FractionallySizedBox 可以根据父容器宽高的百分比来设置子组件宽高等
文章目录
  1. 1. 布局约束
    1. 1.0.1. BoxConstraints (父->子)的约束信息
    2. 1.0.2. ConstrainedBox
      1. 1.0.2.1. SizedBox
      2. 1.0.2.2. 多层约束
    3. 1.0.3. UnconstrainedBox
    4. 1.0.4. 其他约束类