Flutter 布局实战:掌握 Row/Column/Flex 弹性布局

导语

在 Flutter 中,布局即代码 。掌握 RowColumnFlex 这三个基础弹性布局组件,足以应对 80% 以上的 UI 场景。它们不仅灵活高效,还能轻松实现跨平台响应式设计。本文通过两个典型实战案例(登录页 + 商品卡片),深入解析核心属性、使用技巧与常见陷阱,并附完整可运行代码!


一、核心布局组件解析

1. Row ------ 水平布局

  • 作用 :将子组件沿水平方向(主轴)排列。
  • 关键属性
    • mainAxisAlignment:主轴(水平)对齐方式(如 startcenterspaceBetween
    • crossAxisAlignment:交叉轴(垂直)对齐方式(如 startcenterstretch
    • children:子组件列表(必须为非空 List)

2. Column ------ 垂直布局

  • 作用 :将子组件沿垂直方向(主轴)排列。
  • 关键属性 :与 Row 完全一致,仅主轴方向不同(垂直为主轴,水平为交叉轴)。

3. Flex ------ 通用弹性容器

  • 作用RowColumn 的父类,通过 direction 属性动态切换布局方向。
  • 关键属性
    • direction: Axis.horizontal → 等效于 Row
    • direction: Axis.vertical → 等效于 Column

💡 小贴士 :日常开发中优先使用 Row / Column,仅在需要动态切换方向时才用 Flex


二、实战案例 1:登录页面布局

复制代码

dart

编辑

复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const LoginApp());
}

class LoginApp extends StatelessWidget {
  const LoginApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '登录页面布局',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const LoginPage(),
    );
  }
}

class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用户登录')),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center, // 主轴居中(垂直方向)
          children: [
            // 用户名输入框
            TextField(
              decoration: InputDecoration(
                hintText: '请输入用户名',
                prefixIcon: const Icon(Icons.person),
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
              ),
            ),
            const SizedBox(height: 10), // 间距控制

            // 密码输入框
            TextField(
              obscureText: true,
              decoration: InputDecoration(
                hintText: '请输入密码',
                prefixIcon: const Icon(Icons.lock),
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
              ),
            ),
            const SizedBox(height: 20),

            // 登录按钮:占满宽度
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {},
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 15),
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                ),
                child: const Text('登录'),
              ),
            ),
            const SizedBox(height: 15),

            // 忘记密码 / 注册账号
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                TextButton(onPressed: () {}, child: const Text('忘记密码?')),
                TextButton(onPressed: () {}, child: const Text('注册账号')),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

优化说明

  • 使用 SizedBox(width: double.infinity) 替代 Flex + Expanded,更简洁直观
  • 所有间距统一用 SizedBox 控制,避免嵌套 Padding

三、实战案例 2:商品卡片布局

复制代码

dart

编辑

复制代码
Widget productCard() {
  return Container(
    margin: const EdgeInsets.all(10),
    padding: const EdgeInsets.all(10),
    decoration: BoxDecoration(
      border: Border.all(color: Colors.grey.shade200),
      borderRadius: BorderRadius.circular(8),
    ),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start, // 图文顶部对齐
      children: [
        // 商品图片占位
        Container(
          width: 80,
          height: 80,
          color: Colors.grey.shade100,
          alignment: Alignment.center,
          child: const Icon(Icons.image, size: 40, color: Colors.grey),
        ),
        const SizedBox(width: 10),

        // 商品信息区域:自动撑满剩余空间
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 标题(最多两行,溢出省略)
              const Text(
                'Flutter实战教程:跨端开发从入门到精通',
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
              ),
              const SizedBox(height: 5),

              // 价格
              const Text('价格:¥99.00', style: TextStyle(color: Colors.red, fontSize: 15)),
              const SizedBox(height: 5),

              // 销量 + 按钮
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Text('销量:1000+', style: TextStyle(color: Colors.grey, fontSize: 12)),
                  ElevatedButton(
                    onPressed: () {},
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(horizontal: 10),
                      minimumSize: const Size(0, 25),
                    ),
                    child: const Text('加入购物车', style: TextStyle(fontSize: 12)),
                  ),
                ],
              ),
            ],
          ),
        ),
      ],
    ),
  );
}

🔍 布局亮点

  • Expanded 确保文本区自适应剩余宽度,避免溢出
  • crossAxisAlignment: CrossAxisAlignment.start 实现图文顶部对齐
  • TextOverflow.ellipsis 防止长标题破坏布局

四、布局避坑指南(开发者必看!)

问题类型 常见表现 解决方案
溢出错误 RenderFlex children have non-zero flex but incoming width constraints are unbounded Row/Column 中使用 ExpandedFlexible 包裹可伸缩子项
对齐混乱 文字/图标未按预期对齐 明确区分主轴(main axis)与交叉轴(cross axis),根据布局方向选择对齐方式
间距失控 页面松散或拥挤 优先使用 SizedBox 控制固定间距,避免多层 Padding 嵌套
屏幕适配差 在小屏设备上内容被裁剪 结合 MediaQuery.of(context).size 动态计算尺寸,或使用 LayoutBuilder

🛠️ 进阶建议

  • 复杂布局可组合 Row + Column 嵌套(但不宜过深)
  • 对于等分布局,善用 Expanded(flex: n) 设置比例
  • 调试布局时开启 Flutter Inspector(DevTools)可视化查看约束

五、总结

组件 适用场景 使用频率
Row 水平排列(如按钮组、标签栏) ⭐⭐⭐⭐⭐
Column 垂直排列(如表单、列表项) ⭐⭐⭐⭐⭐
Flex 需动态切换方向的通用容器 ⭐⭐

🌟 记住一句话
"主轴决定排列方向,交叉轴决定对齐方式;用 Expanded 防溢出,用 SizedBox 控间距。"

掌握这三大组件,你已具备构建绝大多数 Flutter UI 的能力。下一步可学习 StackWrapListView 等进阶布局,迈向更复杂的界面设计!

相关推荐
一只大侠的侠2 小时前
Flutter开源鸿蒙跨平台训练营 Day 10特惠推荐数据的获取与渲染
flutter·开源·harmonyos
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
renke33646 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端