什么是流式布局
Row/Column的子组件超出主轴空间时会溢出报错(出现黄黑相间的“溢出警告条”)。Wrap会自动换行(或换列),像文字一样流动,所以叫“流式布局”。- 常用于标签列表、照片墙、按钮组等场景。
Wrap的定义
Wrap({
// 主轴方向: 水平(Axis.horizontal)或垂直(Axis.vertical) 默认水平方向
this.direction = Axis.horizontal,
//主轴方向的对齐方式(每一行内部怎么排)
this.alignment = WrapAlignment.start,
// 主同一行(列)中,子组件之间的间距
this.spacing = 0.0,
// 纵向(换行方向)的对齐方式
this.runAlignment = WrapAlignment.start,
// 行与行之间(或列与列之间)的间距
this.runSpacing = 0.0,
// 交叉轴方向的对齐方式
this.crossAxisAlignment = WrapCrossAlignment.start,
// textDirection / verticalDirection 控制排列方向(从左到右/从右到左,从上到下/从下到上)
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
})
| 参数 | 作用 |
|---|---|
| direction | 主轴方向,水平(Axis.horizontal)或垂直(Axis.vertical) |
| spacing | 同一行(列)中,子组件之间的间距 |
| runSpacing | 行与行之间(或列与列之间)的间距 |
| alignment | 主轴方向的对齐方式(每一行内部怎么排) |
| runAlignment | 纵向(换行方向)的对齐方式 |
| crossAxisAlignment | 交叉轴方向的对齐方式 |
| textDirection / verticalDirection | 控制排列方向(从左到右/从右到左,从上到下/从下到上) |
代码示例
class WrapPage extends StatelessWidget {
const WrapPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
height: 600,
child: Wrap(
// 主轴方向子widget的间距
spacing: 10,
// 交叉轴方向的间距
runSpacing: 30,
// 主轴方向的对齐方式
alignment: WrapAlignment.end,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Container(width: 70, height: 100, color: Colors.green),
Container(width: 10, height: 100, color: Colors.blue),
Container(width: 70, height: 100, color: Colors.red),
Container(width: 70, height: 80, color: Colors.yellow),
Container(width: 70, height: 80, color: Colors.orange),
Container(width: 10, height: 80, color: Colors.purple),
Container(width: 70, height: 80, color: Colors.brown),
Container(width: 70, height: 100, color: Colors.grey),
Container(width: 170, height: 100, color: Colors.black),
Container(width: 70, height: 10, color: Colors.pink),
Container(width: 20, height: 100, color: Colors.cyan),
Container(width: 70, height: 10, color: Colors.lime),
],
),
),
);
}
}
runAlignment 和 crossAxisAlignment 的区别
crossAxisAlignment:
控制 每一行内 元素在交叉轴上的对齐方式。
类似于
Row的crossAxisAlignment。生效范围:单行内部的元素。
例子(
Axis.horizontal):所有元素都贴在行顶部对齐。在一行内。crossAxisAlignment: start ┌─────────────── 容器 ──────────────┐ [█ ] [██ ] [███ ]
runAlignment:
控制 整行之间 在交叉轴方向的对齐方式。
影响的是多行之间的分布,不是单行内的对齐。
类似于
Column的mainAxisAlignment(因为换行后每一行就像一个 “子Column”)。例子(
Axis.horizontal):交叉轴方向(垂直)靠下+--------------------+ ← 空白 [AAA AAA AAA] ← 第一行 [BBB BBB BBB] ← 第二行 [CCC CCC] ← 第三行 +--------------------+runAlignment成立的前提是,Wrap的大小,要大于Wrap内所有元素的和
SizedBox
SizedBox 主要是创建一个固定尺寸的容器
SizedBox(width: 300, height: 300) 表示它的宽度固定为 300,高度固定为 300。
区别于 Container
SizedBox专注于固定宽高和占位,本身没有背景色、边框等修饰属性。Container功能更多(可以设置背景色、边框、内外边距等)。
SizedBox 也可以只指定宽度或高度,用来占据特定的空间
SizedBox(height: 20); // 相当于竖直方向加一个间距
SizedBox(width: 10); // 横向间距
特殊构造函数
SizedBox.shrink():尺寸为 0×0,通常用来“占位但不显示”。SizedBox.expand():尽可能填满父组件给的约束空间。
约束传递(constraints passing)
Flutter 布局的三步规则:
- 父传约束(constraints)给子
- 子在约束范围内确定自己的大小(size)
- 父根据子大小和对齐方式来摆放(position)
SizedBox(height: 600) 的行为
SizedBox(height: 600)会生成一个BoxConstraints,相当于:minHeight = 600 maxHeight = 600也就是固定高度。
这个约束会被直接传递给它的子组件,比如:
SizedBox(height: 600, child: Wrap(...), );→
Wrap收到的约束就是 “必须正好 600 高度”
→Wrap再把这个约束传给它的孩子们(但会根据自己的布局规则调整)。
Container 的行为
- 如果你给
Container设置了height/width,它的表现和SizedBox是一样的:直接在约束中锁定对应尺寸。 - 如果没设置尺寸,
Container会尽可能去匹配父给的约束,可能会变成Expanded一样的拉伸行为,或是和内容一样大(取决于父的约束)。
wrap的默认大小
Wrap 在 Flutter 中的默认大小有两个要点:
- 主轴方向(
direction)默认是水平的- 主轴方向的大小会自适应内容宽度,直到撑满父容器的约束为止。
- 如果父容器给了无限空间(比如
SingleChildScrollView横向包裹),那它会根据子元素的总宽度来定。
- 交叉轴方向(垂直方向,如果
direction是水平)默认也是自适应内容- 它会计算出所有行的总高度(每行的高度 +
runSpacing),正好包裹所有行。
- 它会计算出所有行的总高度(每行的高度 +
总结:
- 如果没有父类的约束,那么Wrap的大小就是子元素的总和
- 如果有父类的约束,那么Wrap的大小就是父约束指定的大小