布局组件详解

布局组件是 Flutter 应用的基础,理解如何使用它们来构建复杂的用户界面至关重要。本教程将详细介绍 Flutter 中最常用的布局组件。

重要提示:Flutter 3.27+ 引入了 Row 和 Column 的 spacing 属性,大大简化了布局代码。

1. Container 容器组件

Container 是最常用的布局组件之一,它是一个多功能组合 Widget,可以包含子 Widget、设置内边距、外边距、装饰等。

核心属性

属性 类型 说明
alignment AlignmentGeometry 子组件的对齐方式
padding EdgeInsetsGeometry 内边距
margin EdgeInsetsGeometry 外边距
decoration BoxDecoration 装饰(背景、边框等)
width/height double 宽度和高度
constraints BoxConstraints 尺寸约束
transform Matrix4 变换矩阵

使用示例

// 基本 Container
Container(
  padding: const EdgeInsets.all(16),
  margin: const EdgeInsets.symmetric(horizontal: 10),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.2),
        blurRadius: 8,
        spreadRadius: 2,
      ),
    ],
  ),
  child: const Text(
    'Hello, Container!',
    style: TextStyle(color: Colors.white),
  ),
)

// 带约束的 Container
Container(
  constraints: const BoxConstraints(
    minWidth: 100,
    maxWidth: 200,
    minHeight: 50,
    maxHeight: 100,
  ),
  decoration: BoxDecoration(
    color: Colors.orange,
    borderRadius: BorderRadius.circular(12),
  ),
  child: const Center(
    child: Text(
      '约束容器',
      style: TextStyle(color: Colors.white),
    ),
  ),
)

// 带变换的 Container
Container(
  padding: const EdgeInsets.all(20),
  decoration: BoxDecoration(
    color: Colors.purple,
    borderRadius: BorderRadius.circular(16),
  ),
  transform: Matrix4.rotationZ(0.1),
  child: const Text(
    '旋转容器',
    style: TextStyle(color: Colors.white),
  ),
)
技巧:使用 const 构造函数可以提高性能,Container 在 Flutter 3.27 中更加高效。

2. Row 行布局

Row Widget 在水平方向上排列子 Widget,是构建水平布局的基础组件。

核心属性

属性 类型 说明
mainAxisAlignment MainAxisAlignment 主轴对齐方式
crossAxisAlignment CrossAxisAlignment 交叉轴对齐方式
mainAxisSize MainAxisSize 主轴尺寸
verticalDirection VerticalDirection 垂直方向
textDirection TextDirection 文本方向
textBaseline TextBaseline 文本基线

使用示例

// 基本 Row
Row(
  children: const [
    Text('A'),
    Text('B'),
    Text('C'),
  ],
)

// 主轴居中对齐
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: const [
    Text('左'),
    Text('中'),
    Text('右'),
  ],
)

// 交叉轴居中对齐
Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Container(
      width: 50,
      height: 30,
      color: Colors.red,
    ),
    Container(
      width: 50,
      height: 60,
      color: Colors.green,
    ),
    Container(
      width: 50,
      height: 90,
      color: Colors.blue,
    ),
  ],
)

// 使用 Spacing(Flutter 3.27+ 新特性)
Row(
  spacing: 10,
  children: [
    const Icon(Icons.star, color: Colors.yellow),
    const Icon(Icons.star, color: Colors.yellow),
    const Icon(Icons.star, color: Colors.yellow),
    const Icon(Icons.star_half, color: Colors.yellow),
    const Icon(Icons.star_border, color: Colors.yellow),
  ],
)

// Expanded 和 Flexible
Row(
  children: [
    Expanded(
      flex: 2,
      child: Container(
        color: Colors.blue,
        height: 100,
        child: const Center(child: Text('2倍')),
      ),
    ),
    Expanded(
      flex: 1,
      child: Container(
        color: Colors.red,
        height: 100,
        child: const Center(child: Text('1倍')),
      ),
    ),
  ],
)
技巧:在 Flutter 3.27+ 中,使用 spacing 属性替代 SizedBox 可以让代码更简洁。

3. Column 列布局

Column Widget 在垂直方向上排列子 Widget,与 Row 类似,只是方向相反。

使用示例

// 基本 Column
Column(
  children: const [
    Text('第一行'),
    Text('第二行'),
    Text('第三行'),
  ],
)

// 完整的表单布局
Column(
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: [
    const Text(
      '登录表单',
      style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
    ),
    const SizedBox(height: 20),
    const TextField(
      decoration: InputDecoration(
        labelText: '用户名',
        border: OutlineInputBorder(),
        prefixIcon: Icon(Icons.person),
      ),
    ),
    const SizedBox(height: 16),
    const TextField(
      decoration: InputDecoration(
        labelText: '密码',
        border: OutlineInputBorder(),
        prefixIcon: Icon(Icons.lock),
      ),
      obscureText: true,
    ),
    const SizedBox(height: 20),
    ElevatedButton(
      onPressed: () {},
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(vertical: 16),
      ),
      child: const Text('登录'),
    ),
  ],
)

// 使用 Spacing(Flutter 3.27+)
Column(
  spacing: 16,
  children: [
    Container(
      height: 50,
      color: Colors.red,
    ),
    Container(
      height: 50,
      color: Colors.green,
    ),
    Container(
      height: 50,
      color: Colors.blue,
    ),
  ],
)

4. Stack 堆叠布局

Stack Widget 允许子 Widget 重叠,可以用来实现复杂的层叠效果,如图片上的文字覆盖、悬浮按钮等。

核心属性

属性 类型 说明
alignment AlignmentGeometry 未定位子组件的对齐方式
textDirection TextDirection 文本方向
fit StackFit 子组件如何适应堆叠
clipBehavior Clip 裁剪行为

使用示例

// 基本 Stack
Stack(
  alignment: Alignment.center,
  children: [
    Container(
      width: 200,
      height: 200,
      color: Colors.blue,
    ),
    Container(
      width: 150,
      height: 150,
      color: Colors.red,
    ),
    Container(
      width: 100,
      height: 100,
      color: Colors.yellow,
    ),
  ],
)

// 使用 Positioned 定位
Stack(
  children: [
    // 背景图片
    Container(
      width: 200,
      height: 200,
      decoration: const BoxDecoration(
        image: DecorationImage(
          image: NetworkImage('https://via.placeholder.com/200'),
          fit: BoxFit.cover,
        ),
        borderRadius: BorderRadius.all(Radius.circular(10)),
      ),
    ),
    // 标题在顶部
    Positioned(
      top: 10,
      left: 10,
      right: 10,
      child: Container(
        padding: const EdgeInsets.all(8),
        color: Colors.black54,
        child: const Text(
          '图片标题',
          style: TextStyle(color: Colors.white),
        ),
      ),
    ),
    // 右下角按钮
    Positioned(
      bottom: 10,
      right: 10,
      child: FloatingActionButton(
        mini: true,
        onPressed: () {},
        child: const Icon(Icons.edit),
      ),
    ),
  ],
)

// IndexedStack(只显示一个子组件)
IndexedStack(
  index: _selectedIndex,
  children: [
    Container(color: Colors.red),
    Container(color: Colors.green),
    Container(color: Colors.blue),
  ],
)
技巧:使用 Align Widget 可以更灵活地控制 Stack 中未定位子组件的位置。

5. SizedBox 尺寸盒

SizedBox 是一个简单的固定尺寸盒子,常用于在布局中添加间距或限制子组件大小。

使用示例

// 添加间距
Column(
  children: [
    const Text('第一项'),
    const SizedBox(height: 20),
    const Text('第二项'),
  ],
)

// 限制子组件大小
SizedBox(
  width: 200,
  height: 100,
  child: Container(
    color: Colors.blue,
    child: const Center(child: Text('固定大小')),
  ),
)

// 扩展到父组件大小
SizedBox.expand(
  child: Container(
    color: Colors.green,
    child: const Center(child: Text('充满父容器')),
  ),
)

// 收缩到子组件大小
SizedBox.shrink(
  child: Container(
    color: Colors.orange,
    child: const Text('收缩到子组件大小'),
  ),
)

6. Expanded 和 Flexible

Expanded 和 Flexible 用于在 Row 或 Column 中控制子组件如何分配可用空间。

Expanded

// 等分空间
Row(
  children: [
    Expanded(
      child: Container(
        color: Colors.red,
        height: 100,
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.blue,
        height: 100,
      ),
    ),
  ],
)

// 按比例分配空间
Row(
  children: [
    Expanded(
      flex: 1,
      child: Container(
        color: Colors.red,
        height: 100,
      ),
    ),
    Expanded(
      flex: 2,
      child: Container(
        color: Colors.blue,
        height: 100,
      ),
    ),
    Expanded(
      flex: 3,
      child: Container(
        color: Colors.green,
        height: 100,
      ),
    ),
  ],
)

Flexible

// Flexible 允许子组件小于可用空间
Row(
  children: [
    Flexible(
      flex: 1,
      child: Container(
        width: 50,
        color: Colors.red,
        height: 100,
      ),
    ),
    Flexible(
      flex: 2,
      child: Container(
        color: Colors.blue,
        height: 100,
      ),
    ),
  ],
)

// Flexible 的 fit 参数
Row(
  children: [
    Flexible(
      fit: FlexFit.tight,
      flex: 1,
      child: Container(
        color: Colors.red,
        height: 100,
      ),
    ),
    Flexible(
      fit: FlexFit.loose,
      flex: 1,
      child: Container(
        width: 50,
        color: Colors.blue,
        height: 100,
      ),
    ),
  ],
)
注意:Expanded 是 Flexible 的一个特例,相当于 Flexible(fit: FlexFit.tight)。

7. Spacer 间隔器

Spacer 在 Row 或 Column 中创建可调整的空隙,将其他子组件推向两侧。

使用示例

// 将组件推到两端
Row(
  children: const [
    Text('左'),
    Spacer(),
    Text('右'),
  ],
)

// 多个 Spacer 创建等分间距
Row(
  children: const [
    Icon(Icons.home),
    Spacer(),
    Icon(Icons.search),
    Spacer(),
    Icon(Icons.settings),
  ],
)

// Spacer 的 flex 参数
Row(
  children: [
    const Text('左'),
    const Spacer(flex: 2),
    const Text('中'),
    const Spacer(flex: 1),
    const Text('右'),
  ],
)

8. SafeArea 安全区域

SafeArea 确保内容不会被系统 UI(如状态栏、刘海屏、底部导航栏)遮挡。

使用示例

// 基本 SafeArea
SafeArea(
  child: Container(
    color: Colors.blue,
    child: const Text('安全区域内的内容'),
  ),
)

// 自定义安全区域
SafeArea(
  top: true,
  bottom: true,
  left: false,
  right: false,
  minimum: const EdgeInsets.all(16),
  child: Container(
    color: Colors.blue,
    child: const Text('自定义安全区域'),
  ),
)

// 在整个应用中使用
MaterialApp(
  builder: (context, child) {
    return SafeArea(
      child: child!,
    );
  },
  home: const MyHomePage(),
)

9. AspectRatio 宽高比

AspectRatio 强制子组件保持指定的宽高比。

使用示例

// 1:1 正方形
AspectRatio(
  aspectRatio: 1.0,
  child: Container(
    color: Colors.blue,
    width: double.infinity,
    child: const Center(child: Text('1:1')),
  ),
)

// 16:9 视频比例
AspectRatio(
  aspectRatio: 16 / 9,
  child: Container(
    color: Colors.green,
    width: double.infinity,
    child: const Center(child: Text('16:9')),
  ),
)

// 4:3 照片比例
AspectRatio(
  aspectRatio: 4 / 3,
  child: Container(
    color: Colors.red,
    width: double.infinity,
    child: const Center(child: Text('4:3')),
  ),
)

10. Card 卡片组件

Card 是 Material Design 风格的卡片组件,用于组织相关内容。

使用示例

// 基本 Card
Card(
  child: Padding(
    padding: const EdgeInsets.all(16.0),
    child: Column(
      children: const [
        Text('卡片标题', style: TextStyle(fontSize: 18)),
        SizedBox(height: 8),
        Text('卡片内容'),
      ],
    ),
  ),
)

// 带阴影的 Card
Card(
  elevation: 8,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12),
  ),
  child: const Padding(
    padding: EdgeInsets.all(16.0),
    child: Text('带阴影的卡片'),
  ),
)

// 点击效果的 Card
Card(
  elevation: 2,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12),
  ),
  child: InkWell(
    onTap: () {
      print('Card 被点击');
    },
    child: const Padding(
      padding: EdgeInsets.all(16.0),
      child: Text('可点击的卡片'),
    ),
  ),
)

// 图片卡片
Card(
  clipBehavior: Clip.antiAlias,
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12),
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [
      Image.network(
        'https://via.placeholder.com/300x200',
        fit: BoxFit.cover,
      ),
      const Padding(
        padding: EdgeInsets.all(16.0),
        child: Text('图片卡片', style: TextStyle(fontSize: 18)),
      ),
    ],
  ),
)

布局最佳实践

  • 使用 Flutter 3.27+ 的 spacing 属性简化布局代码
  • 合理使用 const 构造函数提高性能
  • 避免过度嵌套,优先考虑提取子 Widget
  • 使用 Padding Widget 而不是 Container 来添加间距
  • 在需要约束的地方使用 ConstrainedBox
  • 使用 LayoutBuilder 响应父组件的约束
  • 使用 MediaQuery 适配不同屏幕尺寸
进阶学习:掌握基础布局后,可以学习 CustomSingleChildLayout 和 CustomMultiChildLayout 来创建自定义布局。