Row和Column嵌套布局技巧

在这里插入图片描述

在实际开发中,单一的水平或垂直布局往往无法满足复杂的界面需求,这时就需要将Row和Column进行嵌套使用。本文将深入探讨Row和Column的嵌套技巧,帮助你构建出更加灵活和美观的界面。

一、嵌套布局的基本原则

1.1 嵌套层次控制

合理的嵌套层次是构建良好布局的关键,建议遵循以下原则:

// 推荐的嵌套层次(≤3层)
Column(
  children: [
    Row(...),           // 第2层
    Row(
      children: [
        Column(...),    // 第3层
        Column(...),    // 第3层
      ],
    ),
  ],
)

// 不推荐的嵌套层次(>3层)
Column(
  children: [
    Row(
      children: [
        Column(
          children: [
            Row(
              children: [
                Column(...),  // 第5层
              ],
            ),
          ],
        ),
      ],
    ),
  ],
)

1.2 嵌套布局的性能考量

过深的嵌套会导致以下问题:

  • 渲染性能下降:Flutter需要递归地传递约束和计算布局
  • 代码可读性降低:难以理解嵌套结构
  • 维护成本增加:修改布局时容易遗漏嵌套层次

解决方案:将复杂的嵌套结构提取为独立的Widget组件。

// 不推荐:深层嵌套
Column(
  children: [
    Row(
      children: [
        Column(
          children: [
            Row(
              children: [
                Text('姓名'),
                Text('张三'),
              ],
            ),
          ],
        ),
      ],
    ),
  ],
)

// 推荐:提取为独立组件
Column(
  children: [
    UserInfoCard(name: '张三', age: 25),
  ],
)

class UserInfoCard extends StatelessWidget {
  final String name;
  final int age;

  const UserInfoCard({
    required this.name,
    required this.age,
  });

  
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            _buildInfoRow('姓名', name),
            _buildInfoRow('年龄', '$age岁'),
          ],
        ),
      ),
    );
  }

  Widget _buildInfoRow(String label, String value) {
    return Row(
      children: [
        Text('$label: '),
        SizedBox(width: 8),
        Text(value, style: TextStyle(fontWeight: FontWeight.bold)),
      ],
    );
  }
}

二、常见嵌套布局模式

2.1 头部+内容+底部布局

这是最常见的页面布局模式,适用于大多数应用页面:

class StandardPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // 头部
          _buildHeader(),
          // 内容区域
          Expanded(
            child: _buildContent(),
          ),
          // 底部
          _buildFooter(),
        ],
      ),
    );
  }

  Widget _buildHeader() {
    return Container(
      height: 56,
      padding: EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border(bottom: BorderSide(color: Colors.grey[300]!)),
      ),
      child: Row(
        children: [
          Icon(Icons.menu),
          SizedBox(width: 16),
          Expanded(child: Text('页面标题')),
          Icon(Icons.search),
        ],
      ),
    );
  }

  Widget _buildContent() {
    return ListView.builder(
      itemCount: 10,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text('列表项 $index'),
          subtitle: Text('这是第 $index 项的描述'),
        );
      },
    );
  }

  Widget _buildFooter() {
    return Container(
      height: 60,
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border(top: BorderSide(color: Colors.grey[300]!)),
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          _buildFooterIcon(Icons.home, '首页', true),
          _buildFooterIcon(Icons.explore, '发现', false),
          _buildFooterIcon(Icons.person, '我的', false),
        ],
      ),
    );
  }

  Widget _buildFooterIcon(IconData icon, String label, bool isActive) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, color: isActive ? Colors.blue : Colors.grey),
        SizedBox(height: 4),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: isActive ? Colors.blue : Colors.grey,
          ),
        ),
      ],
    );
  }
}

2.2 列表项布局

列表项通常包含多个元素,需要合理的嵌套布局:

class ListItem extends StatelessWidget {
  final String title;
  final String subtitle;
  final String time;
  final int unreadCount;

  const ListItem({
    required this.title,
    required this.subtitle,
    required this.time,
    this.unreadCount = 0,
  });

  
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        border: Border(bottom: BorderSide(color: Colors.grey[200]!)),
      ),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 左侧图标
          _buildIcon(),
          SizedBox(width: 12),
          // 中间内容
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      title,
                      style: TextStyle(fontWeight: FontWeight.w600),
                    ),
                    Text(
                      time,
                      style: TextStyle(
                        fontSize: 12,
                        color: Colors.grey,
                      ),
                    ),
                  ],
                ),
                SizedBox(height: 4),
                Text(
                  subtitle,
                  style: TextStyle(
                    fontSize: 14,
                    color: Colors.grey[600],
                  ),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
              ],
            ),
          ),
          // 右侧标记
          if (unreadCount > 0) _buildUnreadBadge(),
        ],
      ),
    );
  }

  Widget _buildIcon() {
    return Container(
      width: 48,
      height: 48,
      decoration: BoxDecoration(
        color: Colors.blue[100],
        borderRadius: BorderRadius.circular(24),
      ),
      child: Icon(Icons.notifications, color: Colors.blue),
    );
  }

  Widget _buildUnreadBadge() {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: Colors.red,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        '$unreadCount',
        style: TextStyle(
          color: Colors.white,
          fontSize: 12,
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  }
}

2.3 卡片布局

卡片是现代UI设计的重要元素,通常包含图片、标题、描述等信息:

class ProductCard extends StatelessWidget {
  final String title;
  final String description;
  final String price;
  final String rating;
  final String imageUrl;

  const ProductCard({
    required this.title,
    required this.description,
    required this.price,
    required this.rating,
    required this.imageUrl,
  });

  
  Widget build(BuildContext context) {
    return Card(
      elevation: 2,
      margin: EdgeInsets.all(8),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 图片区域
          _buildImage(),
          // 信息区域
          Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                SizedBox(height: 8),
                Text(
                  description,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.grey[600],
                  ),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 12),
                // 价格和评分
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      $price',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                        color: Colors.red,
                      ),
                    ),
                    Row(
                      children: [
                        Icon(Icons.star, color: Colors.amber, size: 16),
                        SizedBox(width: 4),
                        Text(rating),
                      ],
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildImage() {
    return Container(
      height: 180,
      width: double.infinity,
      decoration: BoxDecoration(
        color: Colors.grey[200],
      ),
      child: Center(
        child: Icon(Icons.image, size: 60, color: Colors.grey),
      ),
    );
  }
}

三、实战案例:个人资料卡片

下面通过一个完整的个人资料卡片示例,展示复杂的嵌套布局技巧:

class ProfileCard extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 第一层嵌套:头像和基本信息
            Row(
              children: [
                _buildAvatar(),
                SizedBox(width: 16),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        '李明',
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      SizedBox(height: 4),
                      Text(
                        '高级软件工程师',
                        style: TextStyle(
                          fontSize: 14,
                          color: Colors.grey[600],
                        ),
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          Icon(
                            Icons.location_on,
                            size: 16,
                            color: Colors.grey,
                          ),
                          SizedBox(width: 4),
                          Text(
                            '北京市海淀区',
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey,
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.edit),
                  onPressed: () {},
                ),
              ],
            ),
            SizedBox(height: 20),
            // 第二层嵌套:统计信息
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _buildStatItem('项目', '35'),
                _buildStatItem('经验', '8年'),
                _buildStatItem('评价', '4.9'),
              ],
            ),
            SizedBox(height: 20),
            Divider(),
            SizedBox(height: 20),
            // 第三层嵌套:技能标签
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  '专业技能',
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                SizedBox(height: 12),
                Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: [
                    _buildSkillTag('Flutter'),
                    _buildSkillTag('Dart'),
                    _buildSkillTag('HarmonyOS'),
                    _buildSkillTag('React Native'),
                    _buildSkillTag('TypeScript'),
                  ],
                ),
              ],
            ),
            SizedBox(height: 20),
            Divider(),
            SizedBox(height: 20),
            // 第四层嵌套:联系方式
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  '联系方式',
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                SizedBox(height: 12),
                _buildContactRow(Icons.phone, '138****8888'),
                SizedBox(height: 8),
                _buildContactRow(Icons.email, 'liming@example.com'),
                SizedBox(height: 8),
                _buildContactRow(Icons.web, 'www.example.com'),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildAvatar() {
    return Container(
      width: 80,
      height: 80,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.blue, Colors.purple],
        ),
        borderRadius: BorderRadius.circular(40),
      ),
      child: Center(
        child: Text(
          '李',
          style: TextStyle(
            color: Colors.white,
            fontSize: 36,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
    );
  }

  Widget _buildStatItem(String label, String value) {
    return Column(
      children: [
        Text(
          value,
          style: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(height: 4),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey,
          ),
        ),
      ],
    );
  }

  Widget _buildSkillTag(String skill) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(
        color: Colors.blue[50],
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Colors.blue[200]!),
      ),
      child: Text(
        skill,
        style: TextStyle(
          fontSize: 12,
          color: Colors.blue[700],
        ),
      ),
    );
  }

  Widget _buildContactRow(IconData icon, String text) {
    return Row(
      children: [
        Icon(icon, size: 20, color: Colors.grey[600]),
        SizedBox(width: 12),
        Text(
          text,
          style: TextStyle(fontSize: 14, color: Colors.grey[800]),
        ),
      ],
    );
  }
}

四、实战案例:订单详情页

订单详情页是一个典型的复杂嵌套布局示例:

class OrderDetailPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('订单详情')),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 订单状态
            _buildOrderStatus(),
            SizedBox(height: 16),
            // 订单信息
            _buildOrderInfo(),
            SizedBox(height: 16),
            // 商品列表
            _buildProductList(),
            SizedBox(height: 16),
            // 价格明细
            _buildPriceDetail(),
            SizedBox(height: 16),
            // 订单操作
            _buildOrderActions(),
          ],
        ),
      ),
    );
  }

  Widget _buildOrderStatus() {
    return Card(
      color: Colors.green[50],
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Row(
          children: [
            Icon(Icons.check_circle, color: Colors.green, size: 32),
            SizedBox(width: 12),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  '订单已完成',
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                    color: Colors.green[700],
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  '感谢您的购买,期待再次光临',
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.green[600],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildOrderInfo() {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '订单信息',
              style: TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 12),
            _buildInfoRow('订单编号', '20240125001'),
            SizedBox(height: 8),
            _buildInfoRow('下单时间', '2024-01-25 10:30:45'),
            SizedBox(height: 8),
            _buildInfoRow('支付方式', '微信支付'),
            SizedBox(height: 8),
            _buildInfoRow('收货地址', '北京市海淀区中关村大街1号'),
          ],
        ),
      ),
    );
  }

  Widget _buildInfoRow(String label, String value) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SizedBox(
          width: 80,
          child: Text(
            '$label: ',
            style: TextStyle(color: Colors.grey[600]),
          ),
        ),
        Expanded(
          child: Text(
            value,
            style: TextStyle(fontWeight: FontWeight.w500),
          ),
        ),
      ],
    );
  }

  Widget _buildProductList() {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '商品列表',
              style: TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 12),
            _buildProductItem('商品1', '299.00', 2),
            SizedBox(height: 12),
            _buildProductItem('商品2', '199.00', 1),
          ],
        ),
      ),
    );
  }

  Widget _buildProductItem(String name, String price, int count) {
    return Row(
      children: [
        Container(
          width: 60,
          height: 60,
          decoration: BoxDecoration(
            color: Colors.grey[200],
            borderRadius: BorderRadius.circular(8),
          ),
          child: Icon(Icons.shopping_bag),
        ),
        SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                name,
                style: TextStyle(fontWeight: FontWeight.w500),
              ),
              SizedBox(height: 4),
              Text(
                $price × $count',
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.grey[600],
                ),
              ),
            ],
          ),
        ),
        Text(
          ${(double.parse(price) * count).toStringAsFixed(2)}',
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    );
  }

  Widget _buildPriceDetail() {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            _buildPriceRow('商品总额', '797.00'),
            SizedBox(height: 8),
            _buildPriceRow('运费', '0.00'),
            SizedBox(height: 8),
            _buildPriceRow('优惠', '-20.00', color: Colors.red),
            Divider(),
            SizedBox(height: 8),
            _buildPriceRow('实付金额', '777.00', isTotal: true),
          ],
        ),
      ),
    );
  }

  Widget _buildPriceRow(String label, String value, {Color? color, bool isTotal = false}) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(
          label,
          style: TextStyle(
            fontSize: isTotal ? 16 : 14,
            fontWeight: isTotal ? FontWeight.bold : FontWeight.normal,
          ),
        ),
        Text(
          $value',
          style: TextStyle(
            fontSize: isTotal ? 18 : 14,
            fontWeight: FontWeight.bold,
            color: color ?? (isTotal ? Colors.red : Colors.black87),
          ),
        ),
      ],
    );
  }

  Widget _buildOrderActions() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        OutlinedButton(
          onPressed: () {},
          child: Text('申请售后'),
        ),
        SizedBox(width: 12),
        OutlinedButton(
          onPressed: () {},
          child: Text('查看物流'),
        ),
        SizedBox(width: 12),
        ElevatedButton(
          onPressed: () {},
          child: Text('再次购买'),
        ),
      ],
    );
  }
}

五、嵌套布局最佳实践

5.1 提取可复用组件

将重复出现的布局模式提取为独立组件:

// 提取通用的信息行组件
class InfoRow extends StatelessWidget {
  final String label;
  final String value;
  final Color? labelColor;
  final Color? valueColor;

  const InfoRow({
    required this.label,
    required this.value,
    this.labelColor,
    this.valueColor,
  });

  
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SizedBox(
          width: 80,
          child: Text(
            '$label: ',
            style: TextStyle(color: labelColor ?? Colors.grey[600]),
          ),
        ),
        Expanded(
          child: Text(
            value,
            style: TextStyle(
              color: valueColor ?? Colors.black87,
              fontWeight: FontWeight.w500,
            ),
          ),
        ),
      ],
    );
  }
}

// 使用
Column(
  children: [
    InfoRow(label: '姓名', value: '张三'),
    InfoRow(label: '年龄', value: '25'),
    InfoRow(label: '城市', value: '北京'),
  ],
)

5.2 使用const优化性能

对于不变的子组件,使用const构造函数:

Column(
  children: const [
    SizedBox(height: 16),
    Divider(),
    SizedBox(height: 16),
  ],
)

5.3 合理使用Expanded和Flexible

Row(
  children: [
    Container(width: 100, color: Colors.red),  // 固定宽度
    Expanded(
      flex: 2,
      child: Container(color: Colors.green),  // 占据剩余空间
    ),
    Flexible(
      flex: 1,
      child: Container(color: Colors.blue),  // 可灵活伸缩
    ),
  ],
)

5.4 控制嵌套深度

// 不推荐:深层嵌套
Column(
  children: [
    Row(
      children: [
        Column(
          children: [
            Row(
              children: [
                Text('深层嵌套'),
              ],
            ),
          ],
        ),
      ],
    ),
  ],
)

// 推荐:提取组件
Column(
  children: [
    _buildNestedWidget(),
  ],
)

六、常见问题与解决方案

6.1 嵌套布局溢出

问题:多层嵌套后出现布局溢出

解决方案

  • 使用SingleChildScrollView包裹可滚动区域
  • 使用ExpandedFlexible控制子组件尺寸
  • 设置合理的约束条件
Scaffold(
  body: Column(
    children: [
      Container(height: 100, color: Colors.red),
      Expanded(
        child: SingleChildScrollView(
          child: Column(
            children: List.generate(50, (index) {
              return ListTile(title: Text('Item $index'));
            }),
          ),
        ),
      ),
      Container(height: 60, color: Colors.blue),
    ],
  ),
)

6.2 对齐方式不生效

问题:设置了mainAxisAlignment但不生效

原因mainAxisSizemax时,占满父组件空间

解决方案

// 方案1:使用mainAxisSize.min
Row(
  mainAxisSize: MainAxisSize.min,
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Container(width: 100, color: Colors.red),
  ],
)

// 方案2:父组件使用Center
Center(
  child: Row(
    children: [
      Container(width: 100, color: Colors.red),
    ],
  ),
)

6.3 Expanded使用错误

问题:在Column中使用Expanded导致错误

原因:Expanded必须在Row、Column或Flex的直接子组件中使用

解决方案

// 错误用法
Container(
  child: Expanded(
    child: Text('错误'),
  ),
)

// 正确用法
Row(
  children: [
    Expanded(
      child: Text('正确'),
    ),
  ],
)

七、性能优化建议

7.1 避免不必要的重建

将不变的子组件提取为const:

class MyWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Column(
      children: const [
        SizedBox(height: 16),
        _StaticContent(),
        SizedBox(height: 16),
      ],
    );
  }
}

class _StaticContent extends StatelessWidget {
  const _StaticContent();

  
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(16),
      child: Text('静态内容'),
    );
  }
}

7.2 使用ListView.builder替代Column

对于大量子项,使用ListView.builder:

// 不推荐
Column(
  children: List.generate(100, (index) {
    return ListTile(title: Text('Item $index'));
  }),
)

// 推荐
ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    return ListTile(title: Text('Item $index'));
  },
)

八、嵌套布局流程图

下图展示了一个复杂嵌套布局的构建过程:

开始构建页面

创建最外层Column

添加头部Row

头部需要嵌套?

添加头像+信息Column

直接添加头部内容

添加内容区域Expanded

内容需要滚动?

添加SingleChildScrollView

直接添加内容

添加底部Row

底部需要嵌套?

添加多个按钮Column

直接添加底部内容

布局完成

九、嵌套布局检查清单

在构建嵌套布局时,可以参考以下检查清单:

  • 嵌套深度是否控制在3层以内
  • 是否提取了可复用的组件
  • 是否使用了const构造函数
  • 是否正确使用了Expanded和Flexible
  • 是否处理了可能的溢出问题
  • 是否考虑了不同屏幕尺寸的适配
  • 是否使用了合理的间距和对齐方式
  • 代码结构是否清晰易读

十、总结

Row和Column的嵌套使用是构建复杂界面的核心技能。通过合理的嵌套层次、组件提取和性能优化,可以构建出既美观又高效的用户界面。

记住以下要点:

  1. 控制嵌套深度:避免过深的嵌套,提取独立组件
  2. 合理使用Expanded:在需要占据剩余空间时使用
  3. 注意性能优化:使用const构造函数和ListView.builder
  4. 遵循最佳实践:保持代码清晰,提高可维护性

通过不断实践和总结经验,你将能够熟练运用Row和Column的嵌套技巧,构建出各种复杂的界面布局。

十一、完整可运行示例

下面是一个完整的可运行示例,展示了嵌套布局在抖音个人主页中的应用:

import 'package:flutter/material.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '嵌套布局示例',
      theme: ThemeData(useMaterial3: true),
      home: const NestedLayoutDemo(),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        elevation: 0,
        leading: IconButton(icon: const Icon(Icons.arrow_back_ios, color: Colors.white), onPressed: () => Navigator.pop(context)),
        title: const Text('抖音', style: TextStyle(color: Colors.white)),
        actions: [
          IconButton(icon: const Icon(Icons.search, color: Colors.white), onPressed: () {}),
          IconButton(icon: const Icon(Icons.menu, color: Colors.white), onPressed: () {}),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _buildUserInfo(),
            _buildStats(),
            _buildTabs(),
            _buildVideoGrid(),
          ],
        ),
      ),
      bottomNavigationBar: _buildBottomBar(),
    );
  }

  Widget _buildUserInfo() {
    return Container(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Stack(
                children: [
                  Container(
                    width: 96,
                    height: 96,
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.white, width: 2),
                      shape: BoxShape.circle,
                    ),
                    child: ClipOval(
                      child: Container(
                        color: Colors.grey[800],
                        child: const Icon(Icons.person, size: 48, color: Colors.white),
                      ),
                    ),
                  ),
                  Positioned(
                    bottom: 0,
                    right: 0,
                    child: Container(
                      padding: const EdgeInsets.all(4),
                      decoration: const BoxDecoration(
                        color: Colors.blue,
                        shape: BoxShape.circle,
                      ),
                      child: const Icon(Icons.add, color: Colors.white, size: 16),
                    ),
                  ),
                ],
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton(
                            onPressed: () {},
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.red,
                              foregroundColor: Colors.white,
                              padding: const EdgeInsets.symmetric(vertical: 8),
                            ),
                            child: const Text('+ 关注', style: TextStyle(fontSize: 14)),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Container(
                          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                          decoration: BoxDecoration(
                            border: Border.all(color: Colors.white30),
                            borderRadius: BorderRadius.circular(4),
                          ),
                          child: const Row(
                            children: [
                              Icon(Icons.arrow_drop_down, color: Colors.white),
                              SizedBox(width: 4),
                              Icon(Icons.circle, color: Colors.white, size: 8),
                            ],
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    Wrap(
                      spacing: 8,
                      children: [
                        _buildInfoChip('上海'),
                        _buildInfoChip('♂'),
                        _buildInfoChip('水瓶座'),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
          const SizedBox(height: 12),
          const Text(
            '🎬 分享生活中的美好瞬间\n📸 摄影爱好者 | 旅行达人',
            style: TextStyle(color: Colors.white70, fontSize: 13),
          ),
        ],
      ),
    );
  }

  Widget _buildInfoChip(String text) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: Colors.white10,
        borderRadius: BorderRadius.circular(4),
      ),
      child: Text(text, style: const TextStyle(color: Colors.white70, fontSize: 12)),
    );
  }

  Widget _buildStats() {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 16),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          _buildStatItem('获赞', '12.5万'),
          _buildStatItem('关注', '238'),
          _buildStatItem('粉丝', '1.2万'),
        ],
      ),
    );
  }

  Widget _buildStatItem(String label, String value) {
    return Column(
      children: [
        Text(
          value,
          style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 4),
        Text(
          label,
          style: const TextStyle(color: Colors.white60, fontSize: 13),
        ),
      ],
    );
  }

  Widget _buildTabs() {
    return Container(
      decoration: BoxDecoration(
        border: Border(bottom: BorderSide(color: Colors.white10)),
      ),
      child: Row(
        children: [
          Expanded(
            child: Container(
              padding: const EdgeInsets.symmetric(vertical: 12),
              decoration: BoxDecoration(
                border: Border(bottom: BorderSide(color: Colors.white, width: 2)),
              ),
              child: const Center(
                child: Text('作品 6', style: TextStyle(color: Colors.white, fontSize: 14)),
              ),
            ),
          ),
          Expanded(
            child: Container(
              padding: const EdgeInsets.symmetric(vertical: 12),
              child: Center(
                child: Text('喜欢 12', style: TextStyle(color: Colors.white60, fontSize: 14)),
              ),
            ),
          ),
          Expanded(
            child: Container(
              padding: const EdgeInsets.symmetric(vertical: 12),
              child: Center(
                child: Text('收藏 5', style: TextStyle(color: Colors.white60, fontSize: 14)),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildVideoGrid() {
    return Padding(
      padding: const EdgeInsets.all(1),
      child: Column(
        children: [
          Row(
            children: [
              Expanded(child: _buildVideoItem('外滩夜景', '12.5万')),
              Expanded(child: _buildVideoItem('早餐吃什么', '8.6万')),
              Expanded(child: _buildVideoItem('周末露营', '15.2万')),
            ],
          ),
          Row(
            children: [
              Expanded(child: _buildVideoItem('咖啡制作', '6.3万')),
              Expanded(child: _buildVideoItem('城市漫步', '9.8万')),
              Expanded(child: _buildVideoItem('深夜食堂', '11.1万')),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildVideoItem(String title, String plays) {
    return AspectRatio(
      aspectRatio: 9 / 16,
      child: Container(
        margin: const EdgeInsets.all(1),
        color: Colors.grey[900],
        child: Stack(
          fit: StackFit.expand,
          children: [
            Center(
              child: Icon(Icons.play_circle_outline, size: 48, color: Colors.white.withOpacity(0.8)),
            ),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: Container(
                padding: const EdgeInsets.all(8),
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.bottomCenter,
                    end: Alignment.topCenter,
                    colors: [Colors.black.withOpacity(0.8), Colors.transparent],
                  ),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: const TextStyle(color: Colors.white, fontSize: 12),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                    const SizedBox(height: 4),
                    Row(
                      children: [
                        const Icon(Icons.play_arrow, color: Colors.white, size: 12),
                        const SizedBox(width: 2),
                        Text(plays, style: const TextStyle(color: Colors.white70, fontSize: 10)),
                        const Spacer(),
                        const Icon(Icons.favorite_border, color: Colors.white, size: 12),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBottomBar() {
    return Container(
      height: 56,
      color: Colors.black,
      child: Row(
        children: [
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.home, size: 24, color: Colors.grey[600]),
                const SizedBox(height: 2),
                Text('首页', style: TextStyle(color: Colors.grey[600], fontSize: 10)),
              ],
            ),
          ),
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.person, size: 24, color: Colors.white),
                const SizedBox(height: 2),
                const Text('朋友', style: TextStyle(color: Colors.white, fontSize: 10)),
              ],
            ),
          ),
          Expanded(
            child: Container(
              margin: const EdgeInsets.symmetric(vertical: 8),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [Colors.cyan, Colors.blue],
                ),
                borderRadius: BorderRadius.circular(8),
              ),
              child: const Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.add, color: Colors.white, size: 28),
                  SizedBox(height: 2),
                ],
              ),
            ),
          ),
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.message, color: Colors.grey[600], size: 24),
                const SizedBox(height: 2),
                Text('消息', style: TextStyle(color: Colors.grey[600], fontSize: 10)),
              ],
            ),
          ),
          Expanded(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.person, color: Colors.grey[600], size: 24),
                const SizedBox(height: 2),
                Text('我', style: TextStyle(color: Colors.grey[600], fontSize: 10)),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐