这节课是 Flutter页面排版的核心,学会Container(容器)、Row(行)、Column(列)三个组件,就能像搭积木一样拼出任意静态页面的骨架(比如个人中心、商品卡片、登录页排版),也是后续所有复杂布局的基础。

课前回顾

  1. Flutter 页面固定结构:MaterialApp → Scaffold → body,所有布局 / UI 都写在body里;
  2. 一切皆 Widget,布局的本质是Widget 的嵌套(父组件包孩子组件,孩子可以是单个 / 多个);
  3. 后续代码都基于无状态组件(StatelessWidget),直接替换lib/main.dart即可,热重载看效果。

一、最核心的容器:Container

Container是 Flutter 中最常用的布局容器,相当于「一个带样式的盒子」,可以包裹任意子 Widget,给它设置宽高、背景、边距、内边距、圆角、阴影等样式,还能做简单的定位。

核心作用

  1. 给子 Widget 设置统一的样式(比如给 Text 加背景、给 Image 加圆角);
  2. 作为布局的「基础单元」,配合 Row/Column 实现复杂排版;
  3. 单独使用时,可作为页面的分区容器(比如把页面分成顶部 / 中间 / 底部)。

核心属性(必记,高频使用)

表格

属性 作用 常用值 / 类型
width/height 容器宽高 数字(如 200、100)
color 背景颜色 Colors.xxx(如 Colors.white)
margin 容器外部的间距(和其他组件的距离) EdgeInsets.all (10)(全方向 10)、EdgeInsets.only (left:10)(仅左 10)
padding 容器内部的间距(和子 Widget 的距离) 和 margin 用法完全一致
decoration 复杂装饰(渐变 / 圆角 / 阴影,不能和 color 同时用 BoxDecoration () 包裹
alignment 子 Widget 在容器内的对齐方式 Alignment.xxx(如居中 Alignment.center)

实战代码:Container 的基础用法

直接替换main.dart,注释写满了,热重载看效果:

dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Container布局',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Container核心用法")),
      // 页面背景色(避免白色太单调)
      backgroundColor: Colors.grey[100],
      body: Center(
        child: Container(
          // 1. 宽高
          width: 300,
          height: 200,
          // 2. 外部间距:和屏幕四周的距离
          margin: EdgeInsets.all(20),
          // 3. 内部间距:和子组件Text的距离
          padding: EdgeInsets.symmetric(horizontal: 20, vertical: 30), // 水平20,垂直30
          // 4. 复杂装饰:圆角+阴影+背景(替代color)
          decoration: BoxDecoration(
            color: Colors.white, // 背景色
            borderRadius: BorderRadius.circular(16), // 圆角16(数字越大越圆)
            boxShadow: [ // 阴影:数组形式,可加多个阴影
              BoxShadow(
                color: Colors.grey[300]!, // 阴影颜色(!表示非空,空安全要求)
                blurRadius: 10, // 模糊程度(数字越大越模糊)
                spreadRadius: 2, // 扩散程度
                offset: Offset(0, 3), // 偏移:(x,y) 右正下正
              )
            ]
          ),
          // 5. 子组件在容器内的对齐:居中
          alignment: Alignment.center,
          // 子Widget:一个Text
          child: Text(
            "Container盒子",
            style: TextStyle(fontSize: 20, color: Colors.blue),
          ),
        ),
      ),
    );
  }
}

运行效果

屏幕中间出现一个白色圆角盒子,带浅灰色阴影,内部文字居中,盒子和屏幕、文字和盒子都有指定间距,这是日常开发中最常见的卡片样式(比如商品卡片、消息卡片)。

关键注意点

  1. color 和 decoration 互斥:想加圆角 / 阴影时,必须把背景色写在BoxDecoration里,不能单独写color,否则会报错;
  2. margin 和 padding 的区别:记一句话「margin 管外,padding 管内」,新手别搞混;
  3. 宽高可选:如果 Container 放在 Row/Column 里,宽高可能会被布局约束覆盖(后续讲),单独使用时建议指定。

二、线性布局:Row(行)和 Column(列)

RowColumn是 Flutter 的基础线性布局,专门实现横向 / 纵向的多组件排列,比如:

  • Row:导航栏的「图标 + 文字」、商品卡片的「图片 + 价格 + 按钮」(横向排);
  • Column:登录页的「账号输入框 + 密码输入框 + 登录按钮」、个人中心的「头像 + 昵称 + 手机号」(纵向排)。

核心共性

  1. 都是多子组件布局:孩子用children(数组)包裹,可放任意多个 Widget(区别于 Container 的child单孩子);
  2. 都有主轴副轴:布局的核心是「主轴对齐」和「副轴对齐」,这是掌握 Row/Column 的关键
  3. 都支持弹性布局(Expanded):解决「子组件占满剩余空间」的适配问题(后续重点讲)。

先搞懂:主轴和副轴(必记,不搞混)

这是 Row/Column 的核心概念,一句话讲清:

  • 主轴:组件排列的方向(Row 是横向,Column 是纵向);
  • 副轴:和主轴垂直的方向(Row 是纵向,Column 是横向)。

表格

布局 主轴方向 副轴方向 核心对齐属性(主轴) 核心对齐属性(副轴)
Row 横向(左右) 纵向(上下) mainAxisAlignment crossAxisAlignment
Column 纵向(上下) 横向(左右) mainAxisAlignment crossAxisAlignment

1. Row(行:横向排列)

核心属性(高频)
  • children:数组,放需要横向排列的子 Widget(必填);
  • mainAxisAlignment主轴(左右)对齐,控制子组件在水平方向的位置;
  • crossAxisAlignment副轴(上下)对齐,控制子组件在垂直方向的位置;
  • margin/padding:和 Container 用法一致,给 Row 整体加外 / 内边距;
  • mainAxisSize:主轴占用空间(MainAxisSize.max占满整行,min自适应子组件宽度,默认 max)。
主轴对齐(mainAxisAlignment)常用值
  • MainAxisAlignment.start:左对齐(默认);
  • MainAxisAlignment.center:水平居中;
  • MainAxisAlignment.end:右对齐;
  • MainAxisAlignment.spaceBetween:两端对齐(子组件之间间距相等,无左右边距);
  • MainAxisAlignment.spaceAround:所有子组件两侧间距相等(左右有一半间距);
  • MainAxisAlignment.spaceEvenly:所有间距(子组件之间 + 左右)完全相等。
实战代码:Row 的横向排列 + 对齐

dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row布局',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Row行布局")),
      body: Container(
        width: double.infinity, // 占满整行宽度
        height: 100,
        color: Colors.grey[100],
        margin: EdgeInsets.all(20),
        child: Row(
          // 主轴(左右):两端对齐
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          // 副轴(上下):垂直居中
          crossAxisAlignment: CrossAxisAlignment.center,
          // 横向排列的3个子Widget(Container+Text+Icon)
          children: [
            // 子1:红色小盒子
            Container(width: 50, height: 50, color: Colors.red),
            // 子2:文字
            Text("Row横向排列", style: TextStyle(fontSize: 18)),
            // 子3:图标
            Icon(Icons.arrow_forward_ios, color: Colors.blue, size: 20),
          ],
        ),
      ),
    );
  }
}

运行效果

一个灰色长条容器内,红色小盒子居左、文字居中、图标居右,三者垂直居中对齐,这是列表项的经典排版(比如消息列表、商品列表的左侧图标 + 中间文字 + 右侧箭头)。

2. Column(列:纵向排列)

核心属性(和 Row 完全一致,只是主轴 / 副轴方向变了)
  • children:数组,放需要纵向排列的子 Widget(必填);
  • mainAxisAlignment主轴(上下)对齐,控制子组件在垂直方向的位置;
  • crossAxisAlignment副轴(左右)对齐,控制子组件在水平方向的位置;
  • 其他属性(margin/padding/mainAxisSize)和 Row 完全一样。
主轴对齐(mainAxisAlignment)常用值

和 Row 一致,只是方向变成上下:start(上对齐,默认)、center(垂直居中)、end(下对齐)、spaceBetween(上下两端对齐)等。

实战代码:Column 的纵向排列 + 对齐

模拟个人中心的头像 + 昵称 + 手机号排版,经典纵向布局:

dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Column布局',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Column列布局")),
      body: Center(
        child: Container(
          width: 200,
          height: 250,
          color: Colors.white,
          margin: EdgeInsets.all(20),
          padding: EdgeInsets.all(30),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(16),
            boxShadow: [BoxShadow(color: Colors.grey[300]!, blurRadius: 8)],
          ),
          child: Column(
            // 主轴(上下):垂直居中
            mainAxisAlignment: MainAxisAlignment.center,
            // 副轴(左右):水平居中
            crossAxisAlignment: CrossAxisAlignment.center,
            // 纵向排列的3个子Widget
            children: [
              // 子1:圆形头像(Container做圆形)
              Container(
                width: 80,
                height: 80,
                decoration: BoxDecoration(
                  color: Colors.blue[100],
                  borderRadius: BorderRadius.circular(40), // 宽高的一半=圆形
                ),
                alignment: Alignment.center,
                child: Icon(Icons.person, color: Colors.blue, size: 40),
              ),
              // 子组件之间的间距:SizedBox(空白盒子,专门用来占位)
              SizedBox(height: 15),
              // 子2:昵称文字
              Text("Flutter学习者", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              // 子组件之间的间距
              SizedBox(height: 8),
              // 子3:手机号文字
              Text("13800138000", style: TextStyle(fontSize: 14, color: Colors.grey[600])),
            ],
          ),
        ),
      ),
    );
  }
}

运行效果

一个白色圆角卡片,内部圆形头像在上、昵称在中、手机号在下,三者全部水平居中,子组件之间有指定间距,这是个人中心 / 用户卡片的经典排版

小技巧:SizedBox(空白占位符)

Flutter 中没有「间距属性」,想给 Row/Column 的子组件之间加间距,用SizedBox

  • 横向间距:SizedBox(width: 10)(Row 中用);
  • 纵向间距:SizedBox(height: 10)(Column 中用);
  • 作用:纯空白的盒子,只占指定宽 / 高,无样式,专门用来做组件间的间距,比 margin 更灵活。

三、Row+Column+Container 嵌套实战(核心重点)

实际开发中,没有单独使用 Row/Column 的场景,都是三者嵌套实现复杂排版 —— 比如「横向排的行里,嵌套纵向排的列;列里又嵌套带样式的容器」。

实战需求:实现一个商品卡片(电商 APP 最常见的布局)

需求拆解
  1. 整体是一个圆角卡片(Container),占满屏幕宽度,有固定高度、阴影;
  2. 卡片内部横向排列(Row):左侧商品图片 + 右侧信息区域;
  3. 右侧信息区域纵向排列(Column):商品名称(上)+ 商品描述(中)+ 价格 + 销量(下,又一个 Row);
  4. 所有元素对齐合理,有指定间距。
完整代码(直接复制运行,注释超详细)

dart

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '商品卡片布局',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("三者嵌套实战-商品卡片")),
      backgroundColor: Colors.grey[100],
      body: Container(
        // 整体卡片的外间距
        margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
        child: Container(
          // 商品卡片主容器:圆角+阴影+背景
          width: double.infinity,
          height: 120,
          padding: EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            boxShadow: [BoxShadow(color: Colors.grey[200]!, blurRadius: 5)],
          ),
          // 卡片内部:横向排列(图片+信息)
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              // 左侧:商品图片容器
              Container(
                width: 100,
                height: 100,
                decoration: BoxDecoration(
                  color: Colors.grey[200],
                  borderRadius: BorderRadius.circular(8),
                  // 网络图片:fit=BoxFit.cover 填充容器,不变形
                  image: DecorationImage(
                    image: NetworkImage("https://picsum.photos/200/200"), // 测试图片
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              // 图片和右侧信息的横向间距
              SizedBox(width: 15),
              // 右侧:信息区域(纵向排列)
              Expanded( // 关键:占满剩余水平空间(适配屏幕)
                child: Column(
                  // 主轴(上下):顶部对齐,因为信息高度不够
                  mainAxisAlignment: MainAxisAlignment.start,
                  // 副轴(左右):左对齐
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    // 商品名称
                    Text(
                      "Flutter零基础入门教程 全套视频",
                      style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                      maxLines: 1, // 只显示1行
                      overflow: TextOverflow.ellipsis, // 超出部分显示...
                    ),
                    // 间距
                    SizedBox(height: 8),
                    // 商品描述
                    Text(
                      "从环境搭建到项目实战,一步一步教你学Flutter",
                      style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                      maxLines: 2, // 最多显示2行
                      overflow: TextOverflow.ellipsis,
                    ),
                    // 间距
                    SizedBox(height: 10),
                    // 价格+销量(横向排列,嵌套Row)
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        // 价格
                        Text(
                          "¥99.00",
                          style: TextStyle(fontSize: 14, color: Colors.red, fontWeight: FontWeight.bold),
                        ),
                        // 销量
                        Text(
                          "已售1000+",
                          style: TextStyle(fontSize: 12, color: Colors.grey[500]),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

运行效果

一个标准的电商商品卡片,左侧方形图片、右侧文字信息,文字区域内名称单行省略、描述两行省略,底部价格居左、销量居右,适配所有屏幕宽度,是企业开发中直接能用的布局模板

核心知识点:Expanded(弹性扩展)

这是本次实战的关键,解决了 Flutter 布局的适配问题

  1. 作用:只能用在Row/Column的子组件中,让子组件占满主轴方向的剩余空间
  2. 场景:比如本次实战中,右侧信息区域在 Row 里,加了Expanded后,会自动占满「屏幕宽度 - 图片宽度 - 间距」的剩余空间,适配所有手机;
  3. 注意:Row 里的 Expanded 占剩余水平空间,Column 里的占剩余垂直空间
  4. 多个 Expanded 可按比例分配空间(加flex: 1属性,比如 flex:1 和 flex:2,就是 1:2 分配)。

Text 的溢出处理(实战必备)

开发中文字经常会超出容器,必须加溢出处理,否则会报错:

  • maxLines: n:最多显示 n 行;
  • overflow: TextOverflow.ellipsis:超出部分显示...
  • 两者必须配合使用,缺一不可。

四、本节课核心总结(必背)

  1. Container:带样式的「盒子」,核心管宽高、边距、内边距、样式,包裹单个 Widget;

  2. Row/Column:线性布局,核心管横向 / 纵向排列,孩子用children(数组),重点区分主轴 / 副轴对齐

  3. 嵌套原则:Row 里可以嵌套 Column,Column 里可以嵌套 Row,所有组件都可以被 Container 包裹加样式;

  4. 三个实用小部件

    • SizedBox:组件间的间距占位符
    • Expanded:Row/Column 子组件的弹性扩展,解决适配问题;
    • Text溢出:maxLines + overflow,实战必加;
  5. 布局的本质:Widget 的嵌套,遵循「从外到内、从大到小」的思路(先搭大框架,再填小细节)。

五、课后练习(敲代码才是硬道理)

看完不如敲一遍,推荐 3 个练习,从简单到复杂,巩固本节课知识点:

练习 1:简单版

实现「导航栏布局」:Row 包裹「左侧文字(返回)+ 中间文字(页面标题)+ 右侧图标(设置)」,三者分别居左、居中、居右,垂直居中对齐。

练习 2:进阶版

实现「登录页布局」:Column 包裹「两个 TextField 输入框(账号 / 密码)+ 一个登录按钮」,所有组件水平居中,组件间有间距,整体在屏幕垂直居中。

练习 3:实战版

修改本节课的商品卡片代码,添加「右侧收藏图标」(在 Row 的最右侧,和图片、信息区域横向排列),实现「图片 + 信息 + 收藏图标」的三栏布局。

Logo

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

更多推荐