流式布局Wrap

  1. 什么是流式布局
  2. Wrap的定义
  3. 代码示例
  4. runAlignment 和 crossAxisAlignment 的区别
  5. SizedBox
  6. 约束传递(constraints passing)
  7. wrap的默认大小

什么是流式布局

  • 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

  • 控制 每一行内 元素在交叉轴上的对齐方式。

  • 类似于 RowcrossAxisAlignment

  • 生效范围:单行内部的元素。

  • 例子(Axis.horizontal):所有元素都贴在行顶部对齐。在一行内

    crossAxisAlignment: start
    ┌─────────────── 容器 ──────────────┐
    [█   ]
    [██  ]
    [███ ]
    

runAlignment

  • 控制 整行之间 在交叉轴方向的对齐方式。

  • 影响的是多行之间的分布,不是单行内的对齐。

  • 类似于 ColumnmainAxisAlignment(因为换行后每一行就像一个 “子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 布局的三步规则:

  1. 父传约束(constraints)给子
  2. 子在约束范围内确定自己的大小(size)
  3. 父根据子大小和对齐方式来摆放(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 中的默认大小有两个要点:

  1. 主轴方向(direction)默认是水平的
    • 主轴方向的大小会自适应内容宽度,直到撑满父容器的约束为止。
    • 如果父容器给了无限空间(比如 SingleChildScrollView 横向包裹),那它会根据子元素的总宽度来定。
  2. 交叉轴方向(垂直方向,如果 direction 是水平)默认也是自适应内容
    • 它会计算出所有行的总高度(每行的高度 + runSpacing),正好包裹所有行

总结:

  • 如果没有父类的约束,那么Wrap的大小就是子元素的总和
  • 如果有父类的约束,那么Wrap的大小就是父约束指定的大小

×

喜欢就点赞,疼爱就打赏