布局组件详解
布局组件是 Flutter 应用的基础,理解如何使用它们来构建复杂的用户界面至关重要。本教程将详细介绍 Flutter 中最常用的布局组件。
重要提示:Flutter 3.27+ 引入了 Row 和 Column 的 spacing 属性,大大简化了布局代码。
1. Container 容器组件
Container 是最常用的布局组件之一,它是一个多功能组合 Widget,可以包含子 Widget、设置内边距、外边距、装饰等。
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
| alignment | AlignmentGeometry | 子组件的对齐方式 |
| padding | EdgeInsetsGeometry | 内边距 |
| margin | EdgeInsetsGeometry | 外边距 |
| decoration | BoxDecoration | 装饰(背景、边框等) |
| width/height | double | 宽度和高度 |
| constraints | BoxConstraints | 尺寸约束 |
| transform | Matrix4 | 变换矩阵 |
使用示例
// 基本 Container
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
spreadRadius: 2,
),
],
),
child: const Text(
'Hello, Container!',
style: TextStyle(color: Colors.white),
),
)
// 带约束的 Container
Container(
constraints: const BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 50,
maxHeight: 100,
),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Text(
'约束容器',
style: TextStyle(color: Colors.white),
),
),
)
// 带变换的 Container
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(16),
),
transform: Matrix4.rotationZ(0.1),
child: const Text(
'旋转容器',
style: TextStyle(color: Colors.white),
),
)
技巧:使用 const 构造函数可以提高性能,Container 在 Flutter 3.27 中更加高效。
2. Row 行布局
Row Widget 在水平方向上排列子 Widget,是构建水平布局的基础组件。
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
| mainAxisAlignment | MainAxisAlignment | 主轴对齐方式 |
| crossAxisAlignment | CrossAxisAlignment | 交叉轴对齐方式 |
| mainAxisSize | MainAxisSize | 主轴尺寸 |
| verticalDirection | VerticalDirection | 垂直方向 |
| textDirection | TextDirection | 文本方向 |
| textBaseline | TextBaseline | 文本基线 |
使用示例
// 基本 Row
Row(
children: const [
Text('A'),
Text('B'),
Text('C'),
],
)
// 主轴居中对齐
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('左'),
Text('中'),
Text('右'),
],
)
// 交叉轴居中对齐
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 50,
height: 30,
color: Colors.red,
),
Container(
width: 50,
height: 60,
color: Colors.green,
),
Container(
width: 50,
height: 90,
color: Colors.blue,
),
],
)
// 使用 Spacing(Flutter 3.27+ 新特性)
Row(
spacing: 10,
children: [
const Icon(Icons.star, color: Colors.yellow),
const Icon(Icons.star, color: Colors.yellow),
const Icon(Icons.star, color: Colors.yellow),
const Icon(Icons.star_half, color: Colors.yellow),
const Icon(Icons.star_border, color: Colors.yellow),
],
)
// Expanded 和 Flexible
Row(
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
height: 100,
child: const Center(child: Text('2倍')),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.red,
height: 100,
child: const Center(child: Text('1倍')),
),
),
],
)
技巧:在 Flutter 3.27+ 中,使用 spacing 属性替代 SizedBox 可以让代码更简洁。
3. Column 列布局
Column Widget 在垂直方向上排列子 Widget,与 Row 类似,只是方向相反。
使用示例
// 基本 Column
Column(
children: const [
Text('第一行'),
Text('第二行'),
Text('第三行'),
],
)
// 完整的表单布局
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'登录表单',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
const TextField(
decoration: InputDecoration(
labelText: '用户名',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 16),
const TextField(
decoration: InputDecoration(
labelText: '密码',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: const Text('登录'),
),
],
)
// 使用 Spacing(Flutter 3.27+)
Column(
spacing: 16,
children: [
Container(
height: 50,
color: Colors.red,
),
Container(
height: 50,
color: Colors.green,
),
Container(
height: 50,
color: Colors.blue,
),
],
)
4. Stack 堆叠布局
Stack Widget 允许子 Widget 重叠,可以用来实现复杂的层叠效果,如图片上的文字覆盖、悬浮按钮等。
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
| alignment | AlignmentGeometry | 未定位子组件的对齐方式 |
| textDirection | TextDirection | 文本方向 |
| fit | StackFit | 子组件如何适应堆叠 |
| clipBehavior | Clip | 裁剪行为 |
使用示例
// 基本 Stack
Stack(
alignment: Alignment.center,
children: [
Container(
width: 200,
height: 200,
color: Colors.blue,
),
Container(
width: 150,
height: 150,
color: Colors.red,
),
Container(
width: 100,
height: 100,
color: Colors.yellow,
),
],
)
// 使用 Positioned 定位
Stack(
children: [
// 背景图片
Container(
width: 200,
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://via.placeholder.com/200'),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
),
// 标题在顶部
Positioned(
top: 10,
left: 10,
right: 10,
child: Container(
padding: const EdgeInsets.all(8),
color: Colors.black54,
child: const Text(
'图片标题',
style: TextStyle(color: Colors.white),
),
),
),
// 右下角按钮
Positioned(
bottom: 10,
right: 10,
child: FloatingActionButton(
mini: true,
onPressed: () {},
child: const Icon(Icons.edit),
),
),
],
)
// IndexedStack(只显示一个子组件)
IndexedStack(
index: _selectedIndex,
children: [
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
],
)
技巧:使用 Align Widget 可以更灵活地控制 Stack 中未定位子组件的位置。
5. SizedBox 尺寸盒
SizedBox 是一个简单的固定尺寸盒子,常用于在布局中添加间距或限制子组件大小。
使用示例
// 添加间距
Column(
children: [
const Text('第一项'),
const SizedBox(height: 20),
const Text('第二项'),
],
)
// 限制子组件大小
SizedBox(
width: 200,
height: 100,
child: Container(
color: Colors.blue,
child: const Center(child: Text('固定大小')),
),
)
// 扩展到父组件大小
SizedBox.expand(
child: Container(
color: Colors.green,
child: const Center(child: Text('充满父容器')),
),
)
// 收缩到子组件大小
SizedBox.shrink(
child: Container(
color: Colors.orange,
child: const Text('收缩到子组件大小'),
),
)
6. Expanded 和 Flexible
Expanded 和 Flexible 用于在 Row 或 Column 中控制子组件如何分配可用空间。
Expanded
// 等分空间
Row(
children: [
Expanded(
child: Container(
color: Colors.red,
height: 100,
),
),
Expanded(
child: Container(
color: Colors.blue,
height: 100,
),
),
],
)
// 按比例分配空间
Row(
children: [
Expanded(
flex: 1,
child: Container(
color: Colors.red,
height: 100,
),
),
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
height: 100,
),
),
Expanded(
flex: 3,
child: Container(
color: Colors.green,
height: 100,
),
),
],
)
Flexible
// Flexible 允许子组件小于可用空间
Row(
children: [
Flexible(
flex: 1,
child: Container(
width: 50,
color: Colors.red,
height: 100,
),
),
Flexible(
flex: 2,
child: Container(
color: Colors.blue,
height: 100,
),
),
],
)
// Flexible 的 fit 参数
Row(
children: [
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(
color: Colors.red,
height: 100,
),
),
Flexible(
fit: FlexFit.loose,
flex: 1,
child: Container(
width: 50,
color: Colors.blue,
height: 100,
),
),
],
)
注意:Expanded 是 Flexible 的一个特例,相当于 Flexible(fit: FlexFit.tight)。
7. Spacer 间隔器
Spacer 在 Row 或 Column 中创建可调整的空隙,将其他子组件推向两侧。
使用示例
// 将组件推到两端
Row(
children: const [
Text('左'),
Spacer(),
Text('右'),
],
)
// 多个 Spacer 创建等分间距
Row(
children: const [
Icon(Icons.home),
Spacer(),
Icon(Icons.search),
Spacer(),
Icon(Icons.settings),
],
)
// Spacer 的 flex 参数
Row(
children: [
const Text('左'),
const Spacer(flex: 2),
const Text('中'),
const Spacer(flex: 1),
const Text('右'),
],
)
8. SafeArea 安全区域
SafeArea 确保内容不会被系统 UI(如状态栏、刘海屏、底部导航栏)遮挡。
使用示例
// 基本 SafeArea
SafeArea(
child: Container(
color: Colors.blue,
child: const Text('安全区域内的内容'),
),
)
// 自定义安全区域
SafeArea(
top: true,
bottom: true,
left: false,
right: false,
minimum: const EdgeInsets.all(16),
child: Container(
color: Colors.blue,
child: const Text('自定义安全区域'),
),
)
// 在整个应用中使用
MaterialApp(
builder: (context, child) {
return SafeArea(
child: child!,
);
},
home: const MyHomePage(),
)
9. AspectRatio 宽高比
AspectRatio 强制子组件保持指定的宽高比。
使用示例
// 1:1 正方形
AspectRatio(
aspectRatio: 1.0,
child: Container(
color: Colors.blue,
width: double.infinity,
child: const Center(child: Text('1:1')),
),
)
// 16:9 视频比例
AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.green,
width: double.infinity,
child: const Center(child: Text('16:9')),
),
)
// 4:3 照片比例
AspectRatio(
aspectRatio: 4 / 3,
child: Container(
color: Colors.red,
width: double.infinity,
child: const Center(child: Text('4:3')),
),
)
10. Card 卡片组件
Card 是 Material Design 风格的卡片组件,用于组织相关内容。
使用示例
// 基本 Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: const [
Text('卡片标题', style: TextStyle(fontSize: 18)),
SizedBox(height: 8),
Text('卡片内容'),
],
),
),
)
// 带阴影的 Card
Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('带阴影的卡片'),
),
)
// 点击效果的 Card
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: InkWell(
onTap: () {
print('Card 被点击');
},
child: const Padding(
padding: EdgeInsets.all(16.0),
child: Text('可点击的卡片'),
),
),
)
// 图片卡片
Card(
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Image.network(
'https://via.placeholder.com/300x200',
fit: BoxFit.cover,
),
const Padding(
padding: EdgeInsets.all(16.0),
child: Text('图片卡片', style: TextStyle(fontSize: 18)),
),
],
),
)
布局最佳实践
- 使用 Flutter 3.27+ 的 spacing 属性简化布局代码
- 合理使用 const 构造函数提高性能
- 避免过度嵌套,优先考虑提取子 Widget
- 使用 Padding Widget 而不是 Container 来添加间距
- 在需要约束的地方使用 ConstrainedBox
- 使用 LayoutBuilder 响应父组件的约束
- 使用 MediaQuery 适配不同屏幕尺寸
进阶学习:掌握基础布局后,可以学习 CustomSingleChildLayout 和 CustomMultiChildLayout 来创建自定义布局。