📋 学习前提
- 已完成第3课:Widget基础
- 理解StatelessWidget和StatefulWidget
- 熟悉Dart语言基础语法
🎯 课程目标
通过本课程,你将能够:
- 掌握Flutter核心布局组件的使用方法
- 理解Flex布局和约束系统的工作原理
- 熟练使用Row、Column进行线性布局
- 掌握Stack的层叠布局技巧
- 构建复杂的响应式布局
🚀 Flutter布局系统概述
Flutter的布局系统基于约束(Constraints)和尺寸(Size)的概念:
布局流程的三个阶段
- 约束传递(Constraints Pass):父Widget向子Widget传递约束条件
- 尺寸确定(Size Determination):子Widget根据约束确定自己的尺寸
- 位置确定(Position Determination):父Widget根据子Widget的尺寸确定位置
💡 重要概念:约束系统
Flutter使用约束系统来布局Widget。每个Widget都会收到来自父Widget的约束(最小/最大宽度和高度),然后在这个约束范围内确定自己的尺寸。
常用布局组件分类
- 单子布局:Container、Center、Padding、Align等
- 多子布局:Row、Column、Stack、Flex等
- 滑动布局:ListView、GridView、CustomScrollView等
- 特殊布局:Table、Wrap、Flow等
📦 Container(容器组件)
Container的基本用法
Container是最常用的布局组件,可以设置尺寸、边距、填充、装饰等:
import 'package:flutter/material.dart';
class ContainerExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
// 尺寸设置
width: 200,
height: 100,
// 边距和填充
margin: const EdgeInsets.all(20), // 外边距
padding: const EdgeInsets.all(16), // 内边距
// 装饰效果
decoration: BoxDecoration(
color: Colors.blue, // 背景颜色
borderRadius: BorderRadius.circular(12), // 圆角
border: Border.all(
color: Colors.black,
width: 2,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(2, 4),
),
],
),
// 子Widget
child: const Text(
'Hello Container',
style: TextStyle(color: Colors.white, fontSize: 18),
),
);
}
}
Container的约束行为
Container在没有子Widget和有子Widget时的行为不同:
// 没有子Widget:尝试尽可能大(在约束范围内)
Container(
color: Colors.red,
width: double.infinity, // 无限宽度
height: 100,
)
// 有子Widget:包裹子Widget的大小
Container(
color: Colors.blue,
child: Text('这个Container会包裹文本大小'),
)
// 同时指定尺寸和子Widget:使用指定尺寸
Container(
width: 200,
height: 100,
child: Text('这个Container使用固定尺寸'),
)
💡 最佳实践:合理使用Container
Container功能强大但相对较重,对于简单的装饰效果,可以考虑使用更轻量的组件如Padding、DecoratedBox等。
➡️ Row(水平布局)
Row的基本用法
Row用于在水平方向上排列子Widget:
Row(
// 主轴对齐方式
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// 交叉轴对齐方式
crossAxisAlignment: CrossAxisAlignment.center,
// 主轴尺寸行为
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 50,
height: 50,
color: Colors.red,
child: const Center(child: Text('1')),
),
Container(
width: 50,
height: 70,
color: Colors.green,
child: const Center(child: Text('2')),
),
Container(
width: 50,
height: 30,
color: Colors.blue,
child: const Center(child: Text('3')),
),
],
)
主轴对齐方式(mainAxisAlignment)
- MainAxisAlignment.start:从开始位置对齐
- MainAxisAlignment.center:居中对齐
- MainAxisAlignment.end:从结束位置对齐
- MainAxisAlignment.spaceBetween:均匀分布,首尾无间距
- MainAxisAlignment.spaceAround:均匀分布,首尾有间距
- MainAxisAlignment.spaceEvenly:完全均匀分布
交叉轴对齐方式(crossAxisAlignment)
- CrossAxisAlignment.start:顶部对齐
- CrossAxisAlignment.center:垂直居中对齐
- CrossAxisAlignment.end:底部对齐
- CrossAxisAlignment.stretch:拉伸填满交叉轴
- CrossAxisAlignment.baseline:基线对齐(用于文本)
⬇️ Column(垂直布局)
Column的基本用法
Column用于在垂直方向上排列子Widget:
Column(
// 主轴对齐方式(垂直方向)
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// 交叉轴对齐方式(水平方向)
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: 60,
color: Colors.red,
child: const Center(child: Text('标题')),
),
Container(
height: 100,
color: Colors.green,
child: const Center(child: Text('内容')),
),
Container(
height: 40,
color: Colors.blue,
child: const Center(child: Text('底部')),
),
],
)
Column的常见布局模式
// 表单布局
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('用户名:', style: TextStyle(fontWeight: FontWeight.bold)),
TextField(decoration: const InputDecoration(hintText: '请输入用户名')),
const SizedBox(height: 16),
const Text('密码:', style: TextStyle(fontWeight: FontWeight.bold)),
TextField(decoration: const InputDecoration(hintText: '请输入密码'), obscureText: true),
const SizedBox(height: 24),
ElevatedButton(onPressed: () {}, child: const Text('登录')),
],
)
// 卡片列表布局
Column(
children: [
Card(
child: ListTile(
leading: const Icon(Icons.person),
title: const Text('张三'),
subtitle: const Text('前端工程师'),
trailing: const Icon(Icons.arrow_forward),
onTap: () {},
),
),
Card(
child: ListTile(
leading: const Icon(Icons.person),
title: const Text('李四'),
subtitle: const Text('后端工程师'),
trailing: const Icon(Icons.arrow_forward),
onTap: () {},
),
),
],
)
📐 Expanded和Flexible
Expanded(扩展组件)
Expanded让子Widget填充Row或Column的剩余空间:
Row(
children: [
Container(
width: 50,
height: 50,
color: Colors.red,
),
Expanded(
flex: 2, // 权重为2
child: Container(
height: 50,
color: Colors.green,
child: const Center(child: Text('占剩余空间的2/3')),
),
),
Expanded(
flex: 1, // 权重为1
child: Container(
height: 50,
color: Colors.blue,
child: const Center(child: Text('占剩余空间的1/3')),
),
),
],
)
Flexible(灵活组件)
Flexible比Expanded更灵活,可以控制是否填满剩余空间:
Column(
children: [
Flexible(
flex: 1,
fit: FlexFit.tight, // 填满剩余空间(同Expanded)
child: Container(color: Colors.red, child: const Center(child: Text('填满'))),
),
Flexible(
flex: 1,
fit: FlexFit.loose, // 不强制填满,按内容大小
child: Container(
color: Colors.green,
child: const Center(child: Text('不填满')),
),
),
Container(
height: 50,
color: Colors.blue,
child: const Center(child: Text('固定高度')),
),
],
)
💡 重要概念:flex权重
flex属性决定子Widget在剩余空间中的分配比例。例如,flex为2:1的两个Expanded,第一个将获得2/3的空间,第二个获得1/3的空间。
🔄 Stack(层叠布局)
Stack的基本用法
Stack用于将子Widget层叠在一起:
Stack(
// 尺寸行为
fit: StackFit.expand, // 填满父容器
// 对齐方式
alignment: Alignment.center,
children: [
// 背景层
Container(
color: Colors.grey[300],
),
// 内容层
const Positioned(
top: 20,
left: 20,
child: Text('左上角文字'),
),
// 浮动按钮层
Positioned(
bottom: 20,
right: 20,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
),
// 居中层
const Center(
child: Text('居中文字', style: TextStyle(fontSize: 24)),
),
],
)
Positioned组件
Positioned用于在Stack中精确定位子Widget:
Stack(
children: [
// 使用具体位置
Positioned(
top: 10,
left: 10,
child: Container(color: Colors.red, width: 50, height: 50),
),
// 使用百分比位置
Positioned.fill(
top: 20, // 距顶部20%
left: 20, // 距左侧20%
child: Container(color: Colors.green),
),
// 使用fromRect
Positioned.fromRect(
rect: const Rect.fromLTWH(100, 100, 80, 60),
child: Container(color: Colors.blue),
),
],
)
📝 示例:头像徽章效果
Stack(
children: [
// 头像
Container(
width: 80,
height: 80,
decoration: const BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage('https://example.com/avatar.jpg'),
fit: BoxFit.cover,
),
),
),
// 在线状态徽章
Positioned(
bottom: 0,
right: 0,
child: Container(
width: 20,
height: 20,
decoration: const BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.fromBorderSide(
BorderSide(color: Colors.white, width: 2),
),
),
),
),
],
)
📚 实践练习
练习1:创建个人资料卡片
使用Column和Row创建个人资料卡片:
- 顶部显示头像和基本信息(使用Row)
- 中间显示个人简介(使用Column)
- 底部显示技能标签(使用Wrap)
- 添加阴影和圆角效果
练习2:实现商品列表项
使用Flex布局实现商品列表项:
- 左侧显示商品图片
- 中间显示商品信息和价格(使用Expanded)
- 右侧显示购买按钮
- 实现响应式布局,适配不同屏幕尺寸
练习3:创建浮动导航栏
使用Stack实现浮动导航栏:
- 底部显示主内容区域
- 在右下角添加浮动按钮
- 实现按钮点击展开菜单效果
- 添加动画过渡效果
🔍 常见问题解答
Q: Row和Column中的子Widget溢出怎么办?
A: 使用Expanded或Flexible让子Widget自适应空间,或者使用SingleChildScrollView包装。
Q: 如何实现等分布局?
A: 使用MainAxisAlignment.spaceEvenly或为每个子Widget包裹Expanded并设置相同的flex值。
Q: Stack中的子Widget如何居中?
A: 使用Alignment.center设置Stack的alignment属性,或者使用Center组件包裹子Widget。
Q: 如何实现响应式布局?
A: 使用MediaQuery获取屏幕尺寸,结合LayoutBuilder根据约束条件动态调整布局。
📖 总结
在本课程中,我们深入学习了Flutter的核心布局组件:
- 掌握了Container的装饰和约束特性
- 熟练使用Row和Column进行线性布局
- 理解了Expanded和Flexible的空间分配机制
- 学会了使用Stack实现层叠布局
- 掌握了各种对齐方式和布局技巧
布局是Flutter UI开发的基础,熟练掌握这些组件将帮助你构建出美观、灵活的界面。