掌握布局三剑客,构建Flutter界面的坚实基础。

你好,欢迎回到《Flutter入门到精通》专栏。在前面的课程中,我们掌握了Widget的核心概念。现在,是时候学习如何使用具体的Widget来搭建用户界面了。本讲将重点介绍三个最基础、最强大的布局Widget:Container、Row和Column

一、万能的容器:Container

Container 可能是Flutter中最常用、最通用的Widget之一。你可以把它想象成一个万能的盒子,它可以装一个子Widget,并对其进行装饰、定位和约束。

1.1 Container 的基本属性

一个最简单的 Container 可以只包含一个子Widget:

dart

Container(
  child: Text('Hello, Container!'),
)

但它的真正威力在于其丰富的属性:

dart

Container(
  // 1. 尺寸属性
  width: 200,
  height: 100,
  // constraints: BoxConstraints(maxWidth: 300), // 更复杂的约束

  // 2. 内边距
  padding: EdgeInsets.all(16.0), // 所有方向内边距为16
  // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), // 对称内边距
  // padding: EdgeInsets.only(left: 8, top: 16), // 仅指定某些方向

  // 3. 外边距
  margin: EdgeInsets.all(24.0), // 所有方向外边距为24

  // 4. 装饰 - 这是让Container变漂亮的关键!
  decoration: BoxDecoration(
    color: Colors.blue, // 背景色
    borderRadius: BorderRadius.circular(12.0), // 圆角
    border: Border.all( // 边框
      color: Colors.black,
      width: 2.0,
    ),
    boxShadow: [ // 阴影
      BoxShadow(
        color: Colors.grey.withOpacity(0.5),
        spreadRadius: 2,
        blurRadius: 5,
        offset: Offset(0, 3), // 阴影位置
      ),
    ],
  ),

  // 5. 对齐方式(当Container有固定尺寸时特别有用)
  alignment: Alignment.center, // 子Widget在Container内居中

  // 6. 子Widget
  child: Text(
    'Styled Container',
    style: TextStyle(color: Colors.white, fontSize: 18),
  ),
)

1.2 实际应用示例

让我们创建一个漂亮的卡片式布局:

dart

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

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(16),
      padding: EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.3),
            blurRadius: 10,
            offset: Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Flutter开发小贴士',
            style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.bold,
              color: Colors.deepPurple,
            ),
          ),
          SizedBox(height: 8),
          Text(
            'Container是一个极其灵活的Widget,可以用于创建卡片、对话框、分隔区域等各种UI元素。',
            style: TextStyle(fontSize: 14, color: Colors.grey[700]),
          ),
        ],
      ),
    );
  }
}

二、线性布局:Row 和 Column

Row 和 Column 是用于在水平和垂直方向上排列子Widget的布局Widget,它们共享相似的属性。

2.1 Row - 水平排列

Row 将其子Widget在水平方向(主轴)上排列。

dart

Row(
  children: <Widget>[
    Icon(Icons.star, color: Colors.orange),
    Icon(Icons.star, color: Colors.orange),
    Icon(Icons.star, color: Colors.orange),
    Icon(Icons.star_border, color: Colors.grey),
    Icon(Icons.star_border, color: Colors.grey),
    SizedBox(width: 8), // 添加间距
    Text('3.0 (128 reviews)'),
  ],
)
Row 的核心属性:
  • mainAxisAlignment - 主轴对齐方式(水平方向):

    • MainAxisAlignment.start:靠左对齐(默认)

    • MainAxisAlignment.end:靠右对齐

    • MainAxisAlignment.center:居中对齐

    • MainAxisAlignment.spaceBetween:子Widget之间平均分布间距

    • MainAxisAlignment.spaceAround:子Widget周围平均分布间距

    • MainAxisAlignment.spaceEvenly:子Widget之间和周围都平均分布间距

  • crossAxisAlignment - 交叉轴对齐方式(垂直方向):

    • CrossAxisAlignment.start:顶部对齐

    • CrossAxisAlignment.end:底部对齐

    • CrossAxisAlignment.center:垂直居中对齐(默认)

    • CrossAxisAlignment.stretch:拉伸子Widget以填满交叉轴

示例:不同的对齐方式

dart

Column(
  children: [
    // 靠左对齐
    Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [Text('Start'), Text('Start')],
    ),
    // 均匀分布
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [Text('Between'), Text('Between')],
    ),
    // 垂直方向拉伸
    Container(
      color: Colors.grey[200],
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Container(width: 50, color: Colors.red),
          Container(width: 50, color: Colors.green),
        ],
      ),
    ),
  ],
)

2.2 Column - 垂直排列

Column 将其子Widget在垂直方向(主轴)上排列。

dart

Column(
  children: <Widget>[
    Text('商品名称', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
    SizedBox(height: 8),
    Text('这是一个很棒的商品描述,可能会很长...'),
    SizedBox(height: 16),
    Text('\$99.99', style: TextStyle(fontSize: 24, color: Colors.red)),
    SizedBox(height: 16),
    ElevatedButton(
      onPressed: () {},
      child: Text('立即购买'),
    ),
  ],
)

Column 的对齐属性与 Row 类似,只是主轴和交叉轴的方向交换了:

  • mainAxisAlignment:现在控制垂直方向的对齐

  • crossAxisAlignment:现在控制水平方向的对齐

2.3 主轴尺寸:mainAxisSize

Row 和 Column 都有一个重要的属性 mainAxisSize

  • MainAxisSize.max:(默认值)在主轴方向上尽可能大(Row占满可用宽度,Column占满可用高度)

  • MainAxisSize.min:在主轴方向上尽可能小(只占用子Widget所需的空间)

dart

// 这个Row只会占用其子Widget所需的宽度,而不是整行宽度
Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.access_time),
    SizedBox(width: 4),
    Text('刚刚'),
  ],
)

三、综合实战:构建一个产品卡片

现在,让我们综合运用 ContainerRow 和 Column 来构建一个完整的产品信息卡片。

dart

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

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(16),
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            blurRadius: 8,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 产品标题和价格行
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                '无线蓝牙耳机',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              Text(
                '\$199',
                style: TextStyle(
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                  color: Colors.blue,
                ),
              ),
            ],
          ),
          
          SizedBox(height: 8),
          
          // 评分和销量行
          Row(
            children: [
              // 评分星星
              Row(
                children: List.generate(5, (index) => 
                  Icon(
                    index < 4 ? Icons.star : Icons.star_border,
                    color: Colors.orange,
                    size: 16,
                  ),
                ),
              ),
              SizedBox(width: 8),
              Text('4.0', style: TextStyle(fontSize: 14)),
              SizedBox(width: 16),
              Text('已售 2.3k', style: TextStyle(fontSize: 14, color: Colors.grey)),
            ],
          ),
          
          SizedBox(height: 12),
          
          // 产品描述
          Text(
            '高品质音效,超长续航,舒适佩戴体验,让你的音乐时刻相伴。',
            style: TextStyle(fontSize: 14, color: Colors.grey[600]),
          ),
          
          SizedBox(height: 16),
          
          // 按钮行
          Row(
            children: [
              Expanded(
                child: OutlinedButton(
                  onPressed: () {},
                  child: Text('加入购物车'),
                ),
              ),
              SizedBox(width: 12),
              Expanded(
                child: ElevatedButton(
                  onPressed: () {},
                  child: Text('立即购买'),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

代码解析:

  1. 外层 Container:创建卡片的视觉效果(背景色、圆角、阴影)。

  2. 主 Column:垂直排列所有内容区块。

  3. 第一个 Row:使用 spaceBetween 让产品名称和价格分居两侧。

  4. 第二个 Row:水平排列评分星星、评分数字和销量信息。

  5. 第三个 Row:使用两个 Expanded Widget让两个按钮平均分配可用宽度。

布局技巧与注意事项

  1. 使用 SizedBox 添加间距:比 Padding 更语义化,专门用于控制尺寸和间距。

  2. 善用 Expanded:在 Row 或 Column 中,让子Widget按比例填充可用空间。

  3. 处理内容溢出:当内容超出可用空间时,Flutter会显示溢出警告(黄色条纹)。解决方案包括:

    • 使用 SingleChildScrollView 包裹

    • 使用 Expanded 或 Flexible

    • 调整字体大小或布局

  4. 调试布局:在复杂布局中,可以给 Container 临时添加颜色,以便可视化查看每个区域的边界。

结语

恭喜!通过本讲的学习,你已经掌握了Flutter布局系统中最重要的三个基础Widget。ContainerRow 和 Column 的组合使用,可以构建出绝大多数常见的界面布局。

记住这些核心概念:

  • Container 用于装饰和约束

  • Row 用于水平排列

  • Column 用于垂直排列

  • 通过 mainAxisAlignment 和 crossAxisAlignment 控制对齐

在下一讲中,我们将继续学习更多强大的布局Widget,包括 Stack(层叠布局)、Expanded 和 Flexible 等,让你能够应对更复杂的布局挑战。


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐