目标
· 理解 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 改成:
import'package:flutter/material.dart';voidmain(){runApp(constMyApp());}classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(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
记录每个值的效果。
答案
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:判断题
题目要求
判断对错,并说明理由:
- Expanded 必须直接放在 Row、Column 或 Flex 的子级中。
- Container 的 padding 是外边距,margin 是内边距。
- Spacer 可以用在 Row 和 Column 中。
- SizedBox 只能用来显示文字。
答案
- 对。
- 错。
- 对。
- 错。
答案解读
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:填空题
题目要求
补全代码,让三个文字“左、中、右”分别贴在两端和中间:
Row(mainAxisAlignment:______,children:const[Text('左'),Text('中'),Text('右'),],)答案
Row(mainAxisAlignment:MainAxisAlignment.spaceBetween,children:const[Text('左'),Text('中'),Text('右'),],)答案解读
spaceBetween 会把第一个和最后一个推到两端,中间的子项等距分布。做顶部栏、底部栏、左右分布时非常常用。
练习6:代码预测题
题目要求
阅读代码,预测屏幕显示效果:
body:Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:const[Text('上'),SizedBox(height:20),Text('下'),],),)答案
“上”和“下”垂直排列,整体在屏幕中间,两者之间有 20 像素间距。
答案解读
Center 让 Column 在页面居中。Column 的 mainAxisAlignment.center 让子项在垂直方向整体居中。SizedBox(height: 20) 提供固定垂直间距。
练习7:改错题
题目要求
下面代码会导致布局溢出,找出来并修正:
Row(children:const[Text('这是一段非常非常长的文字,可能会撑爆屏幕'),Text('这也是一段非常非常长的文字,也可能撑爆屏幕'),],)答案
修正方式之一:
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,文字白色居中:
Container(width:120,height:60,padding:constEdgeInsets.all(______),margin:constEdgeInsets.all(______),decoration:BoxDecoration(color:Colors.blue,borderRadius:BorderRadius.circular(______),),alignment:Alignment.______,child:constText('按钮',style:TextStyle(color:Colors.white),),)答案
Container(width:120,height:60,padding:constEdgeInsets.all(12),margin:constEdgeInsets.all(8),decoration:BoxDecoration(color:Colors.blue,borderRadius:BorderRadius.circular(12),),alignment:Alignment.center,child:constText('按钮',style:TextStyle(color:Colors.white),),)答案解读
padding 是盒子内部留白,margin 是盒子外部留白。BoxDecoration 用来设置背景色和圆角。alignment: Alignment.center 让 child 在盒子内居中。注意:使用 decoration 时,颜色要写在 BoxDecoration 里,不能同时写 color。
练习9:动手编码题:个人信息行
题目要求
用 Row 做一行个人信息:左边一个圆形头像,中间显示姓名和职业,右边一个按钮。要求中间区域用 Expanded 包裹,防止文字太长溢出。
答案
body:Center(child:Padding(padding:constEdgeInsets.all(16),child:Row(children:[Container(width:56,height:56,decoration:constBoxDecoration(color:Colors.blue,shape:BoxShape.circle,),alignment:Alignment.center,child:constText('张',style:TextStyle(color:Colors.white,fontSize:20),),),constSizedBox(width:12),constExpanded(child:Column(crossAxisAlignment:CrossAxisAlignment.start,mainAxisSize:MainAxisSize.min,children:[Text('张三',style:TextStyle(fontSize:18,fontWeight:FontWeight.bold),),SizedBox(height:4),Text('Flutter 开发者'),],),),constSizedBox(width:12),ElevatedButton(onPressed:(){},child:constText('关注'),),],),),)答案解读
Row 水平排列头像、信息、按钮。中间用 Expanded 占剩余宽度,避免长文字溢出。Column 的 crossAxisAlignment.start 让姓名和职业左对齐。mainAxisSize: MainAxisSize.min 让 Column 高度尽量小。圆形头像用 BoxShape.circle。
练习10:动手编码题:上中下布局
题目要求
用 Column 和 Expanded 做上中下布局:顶部固定高度 60,中间占满剩余空间,底部固定高度 60。三个区域用不同颜色区分。
答案
body:Column(children:[Container(height:60,color:Colors.blue,alignment:Alignment.center,child:constText('顶部',style:TextStyle(color:Colors.white)),),Expanded(child:Container(color:Colors.grey.shade200,alignment:Alignment.center,child:constText('中间占满剩余空间'),),),Container(height:60,color:Colors.green,alignment:Alignment.center,child:constText('底部',style:TextStyle(color:Colors.white)),),],)答案解读
Column 垂直排列。顶部和底部用固定高度,中间用 Expanded 吃掉剩余高度。这是 App 页面最经典的骨架:顶部栏 + 内容区 + 底部栏。
练习11:综合挑战:商品卡片
题目要求
做一个商品卡片:外层圆角阴影,内部有内边距。左边一个商品图标,中间显示商品名和描述,右边显示价格和购买按钮。要求使用 Container、Padding、Row、Column、Expanded、SizedBox。
答案
body:Center(child:Container(margin:constEdgeInsets.all(16),padding:constEdgeInsets.all(16),decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(16),boxShadow:[BoxShadow(color:Colors.black.withOpacity(0.1),blurRadius:8,offset:constOffset(0,4),),],),child:Row(children:[Container(width:64,height:64,decoration:BoxDecoration(color:Colors.orange.shade100,borderRadius:BorderRadius.circular(12),),alignment:Alignment.center,child:constIcon(Icons.shopping_bag,color:Colors.orange,size:32),),constSizedBox(width:12),constExpanded(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),),],),),constSizedBox(width:12),Column(mainAxisSize:MainAxisSize.min,children:[constText('¥99',style:TextStyle(fontSize:18,color:Colors.red,fontWeight:FontWeight.bold,),),constSizedBox(height:8),ElevatedButton(onPressed:(){},child:constText('购买'),),],),],),),)答案解读
外层 Container 负责卡片边距、圆角、阴影。内部 Row 分三列:图标、信息、价格按钮。中间信息列用 Expanded 占剩余宽度。右侧价格按钮用 Column 垂直排列。这个练习把本课大部分布局知识综合了起来。
练习12:开放式挑战:音乐播放器界面
题目要求
用 Column、Expanded、Row、Container、Icon 设计一个简单的音乐播放器界面。要求:
· 顶部显示歌曲名;
· 中间 Expanded 区域显示专辑封面占位;
· 底部一排控制按钮:上一首、播放、下一首。
答案示例
body:SafeArea(child:Column(children:[constPadding(padding:EdgeInsets.all(16),child:Text('正在播放:练中学',style:TextStyle(fontSize:20,fontWeight:FontWeight.bold),),),Expanded(child:Container(margin:constEdgeInsets.all(24),decoration:BoxDecoration(color:Colors.deepPurple.shade100,borderRadius:BorderRadius.circular(24),),alignment:Alignment.center,child:constIcon(Icons.music_note,size:80,color:Colors.deepPurple,),),),Padding(padding:constEdgeInsets.all(16),child:Row(mainAxisAlignment:MainAxisAlignment.spaceEvenly,children:[IconButton(onPressed:(){},icon:constIcon(Icons.skip_previous,size:36),),IconButton(onPressed:(){},icon:constIcon(Icons.play_circle_fill,size:64),),IconButton(onPressed:(){},icon:constIcon(Icons.skip_next,size:36),),],),),],),)答案解读
整体用 Column:顶部标题、中间 Expanded 封面、底部控制栏。中间 Expanded 占满剩余空间。底部 Row 用 spaceEvenly 均匀排列按钮。开放题没有唯一答案,重点是布局结构清晰、能运行、能热重载修改。
练习13:错误观察挑战
题目要求
故意制造一次 RenderFlex overflowed 错误:在 Row 中放两个很长的 Text,不要用 Expanded。运行后观察控制台错误,记录关键错误词。
答案
示例错误代码:
Row(children:const[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。学会看这个错误,布局能力会提升很快。
四、本课小结
这一课你练了:
- Row 和 Column 的主轴、交叉轴;
- mainAxisAlignment 和 crossAxisAlignment;
- Container、Padding、Margin、SizedBox;
- Expanded、Spacer 分配剩余空间;
- 用 Row/Column 组合出个人信息行、上中下布局、商品卡片、音乐播放器;
- 通过故意制造溢出,学会看 RenderFlex overflowed 错误。
下一课可以进入 滚动与列表:ListView、SingleChildScrollView、GridView,继续用“练中学”的方式做可滚动页面。