第6章 - 布局系统
嗨,朋友!我是长安。
学会了各种 Widget 后,下一个问题就是:怎么把它们整齐地排列起来? 这就需要用到 Flutter 的布局系统了。
🤔 布局的核心概念
Flutter 的布局思路很简单:用 Widget 包裹 Widget。
布局 Widget
└── 子 Widget 1
└── 子 Widget 2
└── 子 Widget 3
常用的布局 Widget 分为两类:
| 类型 | Widget | 说明 |
|---|---|---|
| 单子布局 | Container、Center、SizedBox、Padding | 只能包含一个子 Widget |
| 多子布局 | Row、Column、Stack、ListView、GridView | 可以包含多个子 Widget |
📏 Row - 水平布局
Row 让子组件水平排列。
Row(
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)
效果:🟥 🟩 🟦 (从左到右排列)
主轴对齐 - mainAxisAlignment
Row(
mainAxisAlignment: MainAxisAlignment.start, // 靠左(默认)
mainAxisAlignment: MainAxisAlignment.center, // 居中
mainAxisAlignment: MainAxisAlignment.end, // 靠右
mainAxisAlignment: MainAxisAlignment.spaceBetween, // 两端对齐
mainAxisAlignment: MainAxisAlignment.spaceAround, // 等间距
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 完全等分
children: [...],
)
图示:
start: |🟥🟩🟦 |
center: | 🟥🟩🟦 |
end: | 🟥🟩🟦|
spaceBetween: |🟥 🟩 🟦|
spaceAround: | 🟥 🟩 🟦 |
spaceEvenly: | 🟥 🟩 🟦 |
交叉轴对齐 - crossAxisAlignment
Row(
crossAxisAlignment: CrossAxisAlignment.start, // 顶部对齐
crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中(默认)
crossAxisAlignment: CrossAxisAlignment.end, // 底部对齐
crossAxisAlignment: CrossAxisAlignment.stretch, // 拉伸填满
children: [...],
)
📐 Column - 垂直布局
Column 让子组件垂直排列,用法和 Row 类似,只是方向变了。
Column(
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)
效果:
🟥
🟩
🟦
记忆技巧
- Row 的主轴是水平方向,交叉轴是垂直方向
- Column 的主轴是垂直方向,交叉轴是水平方向
🔄 Expanded 与 Flexible
当需要子组件按比例填充剩余空间时,使用 Expanded 或 Flexible。
Expanded - 填充剩余空间
Row(
children: [
Container(width: 50, height: 50, color: Colors.red),
Expanded( // 填充剩余空间
child: Container(height: 50, color: Colors.green),
),
Container(width: 50, height: 50, color: Colors.blue),
],
)
效果:🟥 ━━━━━🟩━━━━━ 🟦
flex 属性 - 按比例分配
Row(
children: [
Expanded(
flex: 1, // 占 1 份
child: Container(height: 50, color: Colors.red),
),
Expanded(
flex: 2, // 占 2 份
child: Container(height: 50, color: Colors.green),
),
Expanded(
flex: 1, // 占 1 份
child: Container(height: 50, color: Colors.blue),
),
],
)
效果:红色占 25%,绿色占 50%,蓝色占 25%
📚 Stack - 层叠布局
Stack 让子组件重叠在一起,像扑克牌一样堆叠。
Stack(
children: [
// 底层
Container(
width: 200,
height: 200,
color: Colors.blue,
),
// 中层
Container(
width: 150,
height: 150,
color: Colors.green,
),
// 顶层
Container(
width: 100,
height: 100,
color: Colors.red,
),
],
)
Positioned - 绝对定位
在 Stack 中使用 Positioned 精确控制位置:
Stack(
children: [
Container(width: 300, height: 300, color: Colors.grey[200]),
Positioned(
top: 10,
left: 10,
child: Container(width: 50, height: 50, color: Colors.red),
),
Positioned(
bottom: 10,
right: 10,
child: Container(width: 50, height: 50, color: Colors.blue),
),
Positioned.fill( // 填满整个 Stack
child: Center(
child: Text('居中文字'),
),
),
],
)
📜 ListView - 列表布局
ListView 是可滚动的列表,当内容超出屏幕时会自动滚动。
基础用法
ListView(
children: const [
ListTile(title: Text('项目 1')),
ListTile(title: Text('项目 2')),
ListTile(title: Text('项目 3')),
// ... 更多项目
],
)
ListView.builder - 动态列表(推荐)
当列表项很多时,使用 builder 按需创建,性能更好:
ListView.builder(
itemCount: 100, // 列表项数量
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('$index')),
title: Text('项目 $index'),
subtitle: Text('这是第 $index 项的描述'),
);
},
)
ListView.separated - 带分隔线
ListView.separated(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(title: Text('项目 $index'));
},
separatorBuilder: (context, index) {
return const Divider(); // 分隔线
},
)
🔲 GridView - 网格布局
GridView 用于创建网格布局,如相册、商品列表等。
GridView.count(
crossAxisCount: 3, // 每行 3 个
crossAxisSpacing: 10, // 水平间距
mainAxisSpacing: 10, // 垂直间距
padding: const EdgeInsets.all(10),
children: List.generate(9, (index) {
return Container(
color: Colors.blue[(index + 1) * 100],
child: Center(
child: Text('$index', style: const TextStyle(color: Colors.white)),
),
);
}),
)
GridView.builder - 动态网格
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 每行 2 个
childAspectRatio: 1.5, // 宽高比
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(child: Text('Item $index')),
);
},
)
🎁 Wrap - 自动换行布局
当子组件超出一行时,Wrap 会自动换行,常用于标签列表。
Wrap(
spacing: 8, // 水平间距
runSpacing: 8, // 行间距
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Widget')),
Chip(label: Text('Layout')),
Chip(label: Text('State')),
Chip(label: Text('Navigation')),
],
)
效果:
[Flutter] [Dart] [Widget] [Layout]
[State] [Navigation]
📦 单子布局 Widget
Center - 居中
Center(
child: Text('我会居中'),
)
Padding - 内边距
Padding(
padding: const EdgeInsets.all(16),
child: Text('我有内边距'),
)
SizedBox - 固定尺寸/间距
// 固定尺寸
SizedBox(
width: 100,
height: 100,
child: Container(color: Colors.red),
)
// 作为间距
Column(
children: [
Text('上面'),
SizedBox(height: 20), // 20 像素垂直间距
Text('下面'),
],
)
AspectRatio - 宽高比
AspectRatio(
aspectRatio: 16 / 9, // 16:9 的比例
child: Container(color: Colors.blue),
)
🎯 布局实战示例
示例1:个人资料卡片
Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
// 头像
const CircleAvatar(
radius: 40,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
),
const SizedBox(width: 16),
// 信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'长安',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text(
'Flutter 开发者',
style: TextStyle(color: Colors.grey[600]),
),
const SizedBox(height: 8),
const Wrap(
spacing: 8,
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
],
),
],
),
),
// 操作按钮
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_vert),
),
],
),
),
)
示例2:底部导航布局
Scaffold(
body: const Center(child: Text('内容区域')),
bottomNavigationBar: BottomNavigationBar(
currentIndex: 0,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: '发现'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
],
),
)
📝 小结
这一章我们学习了:
- ✅ Row - 水平布局
- ✅ Column - 垂直布局
- ✅ Stack - 层叠布局
- ✅ Expanded/Flexible - 弹性布局
- ✅ ListView - 可滚动列表
- ✅ GridView - 网格布局
- ✅ Wrap - 自动换行布局
- ✅ 单子布局:Center、Padding、SizedBox、AspectRatio
布局口诀
- 横着排用 Row
- 竖着排用 Column
- 要重叠用 Stack
- 要滚动用 ListView
- 要网格用 GridView
- 要换行用 Wrap
💪 练习题
- 使用 Row 和 Column 创建一个九宫格布局
- 使用 ListView.builder 创建一个消息列表(显示头像、昵称、最后一条消息)
- 使用 Stack 创建一个带角标的图片(如未读消息数量)
点击查看答案
练习1:九宫格
Column(
children: [
Row(
children: [
Expanded(child: Container(height: 100, color: Colors.red)),
Expanded(child: Container(height: 100, color: Colors.green)),
Expanded(child: Container(height: 100, color: Colors.blue)),
],
),
Row(
children: [
Expanded(child: Container(height: 100, color: Colors.yellow)),
Expanded(child: Container(height: 100, color: Colors.purple)),
Expanded(child: Container(height: 100, color: Colors.orange)),
],
),
Row(
children: [
Expanded(child: Container(height: 100, color: Colors.cyan)),
Expanded(child: Container(height: 100, color: Colors.pink)),
Expanded(child: Container(height: 100, color: Colors.teal)),
],
),
],
)
练习3:带角标的图片
Stack(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.message, size: 30),
),
Positioned(
top: -5,
right: -5,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Text(
'3',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
],
)
🚀 下一步
布局系统掌握后,下一章我们来学习 状态管理入门,让应用动起来!
由 编程指南 提供
