【Flutter入门练中学】第2课:布局系统

目标

· 理解 Flutter 布局核心口诀:父传约束,子定尺寸,父定位置。

· 分清 Row / Column 的主轴与交叉轴,掌握 mainAxisAlignment 和 crossAxisAlignment。

· 掌握 Container、Padding、SizedBox、Expanded、Spacer 的用途。

· 能组合实现个人信息行、上中下布局、商品卡片、音乐播放器。

· 能识别并修复 RenderFlex overflowed 溢出错误。

· 通过"题目要求 → 答案 → 答案解读"的多元化习题完成练中学。


一、本课核心概念速览

Flutter 布局口诀:

父传约束,子定尺寸,父定位置。

常用布局 Widget:

Widget 作用

Row 水平排列

Column 垂直排列

Container 盒子:尺寸、颜色、边距、圆角、对齐

Padding 内边距

Margin 外边距,通常用 Container 的 margin

Expanded 按比例占满剩余空间

Flexible 灵活占空间,可松可紧

Spacer 占剩余空间,做间隔

SizedBox 固定宽高或间距

Align / Center 对齐 / 居中

MainAxisAlignment 主轴对齐

CrossAxisAlignment 交叉轴对齐

Row 的主轴是水平,交叉轴是垂直。

Column 的主轴是垂直,交叉轴是水平。


二、最小可运行示例

把 lib/main.dart 改成:

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: const [
              Text('左'),
              Text('中'),
              Text('右'),
            ],
          ),
        ),
      ),
    );
  }
}

按 r 热重载,把 spaceEvenly 改成 spaceBetween、center、end,观察变化。


三、多元化习题

练习0:热重载体验布局

题目要求

运行上面的 Row 示例,依次把 mainAxisAlignment 改成:

· MainAxisAlignment.start

· MainAxisAlignment.center

· MainAxisAlignment.end

· MainAxisAlignment.spaceBetween

· MainAxisAlignment.spaceAround

· MainAxisAlignment.spaceEvenly

记录每个值的效果。

答案

dart 复制代码
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: const [
    Text('左'),
    Text('中'),
    Text('右'),
  ],
)

各值效果:

· start:靠左。

· center:整体居中。

· end:靠右。

· spaceBetween:两端贴边,中间等距。

· spaceAround:每个子项两侧等距。

· spaceEvenly:所有间隔完全相等,包括两端。

答案解读

mainAxisAlignment 控制主轴方向的对齐。Row 的主轴是水平,所以这些值控制水平分布。Column 的主轴是垂直,同样的值会控制垂直分布。热重载可以立刻看到差异,是练布局最快的方式。


练习1:选择题

题目要求

在 Column 中,crossAxisAlignment 控制的是哪个方向?

A. 垂直方向

B. 水平方向

C. 文字颜色

D. 内外边距

答案

B。

答案解读

Column 的主轴是垂直,交叉轴是水平。所以 mainAxisAlignment 控制垂直对齐,crossAxisAlignment 控制水平对齐。Row 刚好相反。


练习2:判断题

题目要求

判断对错,并说明理由:

  1. Expanded 必须直接放在 Row、Column 或 Flex 的子级中。
  2. Container 的 padding 是外边距,margin 是内边距。
  3. Spacer 可以用在 Row 和 Column 中。
  4. SizedBox 只能用来显示文字。

答案

  1. 对。
  2. 错。
  3. 对。
  4. 错。

答案解读

Expanded 依赖 Flex 父级分配剩余空间,所以不能随便放在普通 Container 里。padding 是内边距,margin 是外边距。Spacer 本质是 Expanded(child: SizedBox()),可用在 Row/Column。SizedBox 是尺寸盒子,常用于占位和间距,不显示文字。


练习3:连线题

题目要求

将左侧 Widget 与右侧作用连线:

左侧 右侧

mainAxisAlignment 交叉轴对齐

crossAxisAlignment 主轴对齐

Expanded 固定尺寸或间距

Spacer 按比例占剩余空间

SizedBox 占剩余空间形成间隔

Padding 内边距

答案

· mainAxisAlignment --- 主轴对齐

· crossAxisAlignment --- 交叉轴对齐

· Expanded --- 按比例占剩余空间

· Spacer --- 占剩余空间形成间隔

· SizedBox --- 固定尺寸或间距

· Padding --- 内边距

答案解读

这些是布局系统最常用的成员。先分清主轴和交叉轴,再理解剩余空间怎么分配,布局就不容易乱。


练习4:排序题

题目要求

实现"页面居中显示一个圆角盒子,盒子里有内边距,文字居中"。按从外到内的嵌套顺序排列:

Text、Center、Container、Padding

答案

Center → Container → Padding → Text

答案解读

Center 负责整体居中,Container 负责尺寸、颜色、圆角,Padding 负责文字与盒子边缘的距离,Text 是最内层内容。实际开发中,Container 自带 padding,也可以省掉单独的 Padding。


练习5:填空题

题目要求

补全代码,让三个文字"左、中、右"分别贴在两端和中间:

dart 复制代码
Row(
  mainAxisAlignment: ______,
  children: const [
    Text('左'),
    Text('中'),
    Text('右'),
  ],
)

答案

dart 复制代码
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: const [
    Text('左'),
    Text('中'),
    Text('右'),
  ],
)

答案解读

spaceBetween 会把第一个和最后一个推到两端,中间的子项等距分布。做顶部栏、底部栏、左右分布时非常常用。


练习6:代码预测题

题目要求

阅读代码,预测屏幕显示效果:

dart 复制代码
body: Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: const [
      Text('上'),
      SizedBox(height: 20),
      Text('下'),
    ],
  ),
)

答案

"上"和"下"垂直排列,整体在屏幕中间,两者之间有 20 像素间距。

答案解读

Center 让 Column 在页面居中。Column 的 mainAxisAlignment.center 让子项在垂直方向整体居中。SizedBox(height: 20) 提供固定垂直间距。


练习7:改错题

题目要求

下面代码会导致布局溢出,找出来并修正:

dart 复制代码
Row(
  children: const [
    Text('这是一段非常非常长的文字,可能会撑爆屏幕'),
    Text('这也是一段非常非常长的文字,也可能撑爆屏幕'),
  ],
)

答案

修正方式之一:

dart 复制代码
Row(
  children: const [
    Expanded(
      child: Text(
        '这是一段非常非常长的文字,可能会撑爆屏幕',
        overflow: TextOverflow.ellipsis,
      ),
    ),
    Expanded(
      child: Text(
        '这也是一段非常非常长的文字,也可能撑爆屏幕',
        overflow: TextOverflow.ellipsis,
      ),
    ),
  ],
)

答案解读

Row 主轴方向给子项无限宽度约束,长文字会按自身宽度撑开,超出屏幕就报 RenderFlex overflowed。用 Expanded 让文字按剩余空间分配宽度,再用 overflow: TextOverflow.ellipsis 显示省略号。这是实际开发中最常见的布局修复之一。


练习8:补全代码题

题目要求

补全代码,做一个 120×60 的蓝色圆角盒子,内部有 12 内边距,外边距 8,文字白色居中:

dart 复制代码
Container(
  width: 120,
  height: 60,
  padding: const EdgeInsets.all(______),
  margin: const EdgeInsets.all(______),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(______),
  ),
  alignment: Alignment.______,
  child: const Text(
    '按钮',
    style: TextStyle(color: Colors.white),
  ),
)

答案

dart 复制代码
Container(
  width: 120,
  height: 60,
  padding: const EdgeInsets.all(12),
  margin: const EdgeInsets.all(8),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(12),
  ),
  alignment: Alignment.center,
  child: const Text(
    '按钮',
    style: TextStyle(color: Colors.white),
  ),
)

答案解读

padding 是盒子内部留白,margin 是盒子外部留白。BoxDecoration 用来设置背景色和圆角。alignment: Alignment.center 让 child 在盒子内居中。注意:使用 decoration 时,颜色要写在 BoxDecoration 里,不能同时写 color。


练习9:动手编码题:个人信息行

题目要求

用 Row 做一行个人信息:左边一个圆形头像,中间显示姓名和职业,右边一个按钮。要求中间区域用 Expanded 包裹,防止文字太长溢出。

答案

dart 复制代码
body: Center(
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Row(
      children: [
        Container(
          width: 56,
          height: 56,
          decoration: const BoxDecoration(
            color: Colors.blue,
            shape: BoxShape.circle,
          ),
          alignment: Alignment.center,
          child: const Text(
            '张',
            style: TextStyle(color: Colors.white, fontSize: 20),
          ),
        ),
        const SizedBox(width: 12),
        const Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                '张三',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 4),
              Text('Flutter 开发者'),
            ],
          ),
        ),
        const SizedBox(width: 12),
        ElevatedButton(
          onPressed: () {},
          child: const Text('关注'),
        ),
      ],
    ),
  ),
)

答案解读

Row 水平排列头像、信息、按钮。中间用 Expanded 占剩余宽度,避免长文字溢出。Column 的 crossAxisAlignment.start 让姓名和职业左对齐。mainAxisSize: MainAxisSize.min 让 Column 高度尽量小。圆形头像用 BoxShape.circle。


练习10:动手编码题:上中下布局

题目要求

用 Column 和 Expanded 做上中下布局:顶部固定高度 60,中间占满剩余空间,底部固定高度 60。三个区域用不同颜色区分。

答案

dart 复制代码
body: Column(
  children: [
    Container(
      height: 60,
      color: Colors.blue,
      alignment: Alignment.center,
      child: const Text('顶部', style: TextStyle(color: Colors.white)),
    ),
    Expanded(
      child: Container(
        color: Colors.grey.shade200,
        alignment: Alignment.center,
        child: const Text('中间占满剩余空间'),
      ),
    ),
    Container(
      height: 60,
      color: Colors.green,
      alignment: Alignment.center,
      child: const Text('底部', style: TextStyle(color: Colors.white)),
    ),
  ],
)

答案解读

Column 垂直排列。顶部和底部用固定高度,中间用 Expanded 吃掉剩余高度。这是 App 页面最经典的骨架:顶部栏 + 内容区 + 底部栏。


练习11:综合挑战:商品卡片

题目要求

做一个商品卡片:外层圆角阴影,内部有内边距。左边一个商品图标,中间显示商品名和描述,右边显示价格和购买按钮。要求使用 Container、Padding、Row、Column、Expanded、SizedBox。

答案

dart 复制代码
body: Center(
  child: Container(
    margin: const EdgeInsets.all(16),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(16),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.1),
          blurRadius: 8,
          offset: const Offset(0, 4),
        ),
      ],
    ),
    child: Row(
      children: [
        Container(
          width: 64,
          height: 64,
          decoration: BoxDecoration(
            color: Colors.orange.shade100,
            borderRadius: BorderRadius.circular(12),
          ),
          alignment: Alignment.center,
          child: const Icon(Icons.shopping_bag, color: Colors.orange, size: 32),
        ),
        const SizedBox(width: 12),
        const Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                'Flutter 实战课',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 6),
              Text(
                '从零开始,练中学布局系统',
                style: TextStyle(color: Colors.grey),
              ),
            ],
          ),
        ),
        const SizedBox(width: 12),
        Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text(
              '¥99',
              style: TextStyle(
                fontSize: 18,
                color: Colors.red,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 8),
            ElevatedButton(
              onPressed: () {},
              child: const Text('购买'),
            ),
          ],
        ),
      ],
    ),
  ),
)

答案解读

外层 Container 负责卡片边距、圆角、阴影。内部 Row 分三列:图标、信息、价格按钮。中间信息列用 Expanded 占剩余宽度。右侧价格按钮用 Column 垂直排列。这个练习把本课大部分布局知识综合了起来。


练习12:开放式挑战:音乐播放器界面

题目要求

用 Column、Expanded、Row、Container、Icon 设计一个简单的音乐播放器界面。要求:

· 顶部显示歌曲名;

· 中间 Expanded 区域显示专辑封面占位;

· 底部一排控制按钮:上一首、播放、下一首。

答案示例

dart 复制代码
body: SafeArea(
  child: Column(
    children: [
      const Padding(
        padding: EdgeInsets.all(16),
        child: Text(
          '正在播放:练中学',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
      ),
      Expanded(
        child: Container(
          margin: const EdgeInsets.all(24),
          decoration: BoxDecoration(
            color: Colors.deepPurple.shade100,
            borderRadius: BorderRadius.circular(24),
          ),
          alignment: Alignment.center,
          child: const Icon(
            Icons.music_note,
            size: 80,
            color: Colors.deepPurple,
          ),
        ),
      ),
      Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            IconButton(
              onPressed: () {},
              icon: const Icon(Icons.skip_previous, size: 36),
            ),
            IconButton(
              onPressed: () {},
              icon: const Icon(Icons.play_circle_fill, size: 64),
            ),
            IconButton(
              onPressed: () {},
              icon: const Icon(Icons.skip_next, size: 36),
            ),
          ],
        ),
      ),
    ],
  ),
)

答案解读

整体用 Column:顶部标题、中间 Expanded 封面、底部控制栏。中间 Expanded 占满剩余空间。底部 Row 用 spaceEvenly 均匀排列按钮。开放题没有唯一答案,重点是布局结构清晰、能运行、能热重载修改。


练习13:错误观察挑战

题目要求

故意制造一次 RenderFlex overflowed 错误:在 Row 中放两个很长的 Text,不要用 Expanded。运行后观察控制台错误,记录关键错误词。

答案

示例错误代码:

dart 复制代码
Row(
  children: const [
    Text('这是一段非常非常非常非常非常非常非常长的文字'),
    Text('这也是一段非常非常非常非常非常非常非常长的文字'),
  ],
)

控制台常见错误:

text 复制代码
A RenderFlex overflowed by 123 pixels on the right.

答案解读

RenderFlex 是 Row/Column 的渲染对象。当子项总宽度超过可用宽度时,就会溢出。右侧溢出会提示 on the right,Column 溢出会提示 on the bottom。解决方法:用 Expanded、Flexible、SingleChildScrollView 或 TextOverflow.ellipsis。学会看这个错误,布局能力会提升很快。


四、本课小结

这一课你练了:

  1. Row 和 Column 的主轴、交叉轴;
  2. mainAxisAlignment 和 crossAxisAlignment;
  3. Container、Padding、Margin、SizedBox;
  4. Expanded、Spacer 分配剩余空间;
  5. 用 Row/Column 组合出个人信息行、上中下布局、商品卡片、音乐播放器;
  6. 通过故意制造溢出,学会看 RenderFlex overflowed 错误。

下一课可以进入 滚动与列表:ListView、SingleChildScrollView、GridView,继续用"练中学"的方式做可滚动页面。

相关推荐
扶风ff1 小时前
练题簿小程序:家庭在线练题,课后复习更简单
学习·小程序
彧azz1 小时前
Linux 网络编程学习总结
linux·网络·笔记·学习·面试
一尘之中2 小时前
指挥控制中心与指控系统:概念、应用、厂商与DDS技术全景
学习·架构·ai写作
一条破秋裤2 小时前
测试A学习顺序
学习
传奇开心果编程2 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack
迪丽热爱3 小时前
多媒体应用16-830(补)
学习
潜心一志4 小时前
HALCON软件——基础语法
学习·计算机视觉
xy34534 小时前
Axure 9.0 中继器核心结构
前端·ui·html·axure·原型·产品设计
babe小鑫5 小时前
信息与计算科学专业应届生面试 怎么证明自己能解决业务问题
学习·r语言·excel