Flutter 弹性布局组件详解
弹性布局是 Flutter 中最重要的布局方式之一,通过 Row、Column、Flex、Expanded 和 Flexible 组件,可以创建灵活的自适应界面。
一、核心组件
1. Row - 水平弹性布局
dart
Row(
mainAxisAlignment: MainAxisAlignment.start,// 主轴对齐方式
crossAxisAlignment: CrossAxisAlignment.center,// 交叉轴对齐方式
mainAxisSize: MainAxisSize.max,// 主轴尺寸
textDirection: TextDirection.ltr,// 文字方向
verticalDirection: VerticalDirection.down,// 垂直方向
children: <Widget>[
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)
2. Column - 垂直弹性布局
dart
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch, // 交叉轴拉伸
children: <Widget>[
Container(height: 50, color: Colors.red),
Container(height: 100, color: Colors.green),
Container(height: 150, color: Colors.blue),
],
)
二、主轴对齐方式详解
dart
Column(
children: [
// 1. 起始对齐 (默认)
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
SizedBox(height: 10),
// 2. 居中对齐
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
SizedBox(height: 10),
// 3. 末尾对齐
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
SizedBox(height: 10),
// 4. 均匀分布 - 两端对齐
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
SizedBox(height: 10),
// 5. 均匀分布 - 环绕对齐
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
SizedBox(height: 10),
// 6. 完全均匀分布
Container(
height: 100,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [RedBox(), GreenBox(), BlueBox()],
),
),
],
)
// 辅助组件
Widget RedBox() => Container(width: 50, height: 50, color: Colors.red);
Widget GreenBox() => Container(width: 50, height: 50, color: Colors.green);
Widget BlueBox() => Container(width: 50, height: 50, color: Colors.blue);
三、交叉轴对齐方式详解
dart
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. 起始对齐
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text('Start'), Container(width: 100, height: 30, color: Colors.red)],
),
SizedBox(width: 20),
// 2. 居中对齐
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text('Center'), Container(width: 100, height: 30, color: Colors.green)],
),
SizedBox(width: 20),
// 3. 末尾对齐
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [Text('End'), Container(width: 100, height: 30, color: Colors.blue)],
),
SizedBox(width: 20),
// 4. 拉伸对齐
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [Text('Stretch'), Container(height: 30, color: Colors.orange)],
),
SizedBox(width: 20),
// 5. 基线对齐
Column(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text('Baseline', style: TextStyle(fontSize: 16)),
Text('Align', style: TextStyle(fontSize: 24)),
],
),
],
)
四、Expanded 与 Flexible
1. Expanded - 强制填满剩余空间
dart
Row(
children: [
// 固定宽度
Container(width: 100, height: 100, color: Colors.red),
// Expanded 填满剩余空间
Expanded(
flex: 2,// 占比 2/3
child: Container(height: 100, color: Colors.green),
),
Expanded(
flex: 1,// 占比 1/3
child: Container(height: 100, color: Colors.blue),
),
],
)
2. Flexible - 灵活的空间占用
dart
Row(
children: [
// fit: FlexFit.tight - 类似 Expanded
Flexible(
flex: 1,
fit: FlexFit.tight,// 强制填满
child: Container(height: 50, color: Colors.red),
),
// fit: FlexFit.loose - 按需占用
Flexible(
flex: 1,
fit: FlexFit.loose,// 按需占用
child: Container(
height: 30,// 实际高度30,不会强制填满
color: Colors.green,
),
),
Flexible(
flex: 1,
child: Container(height: 70, color: Colors.blue),
),
],
)
五、Flex 组件(Row/Column 的底层)
dart
// Flex 是 Row 和 Column 的底层实现
Flex(
direction: Axis.horizontal,// 或 Axis.vertical
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// 可以在 Flex 中混合使用 Expanded 和普通组件
Container(width: 50, height: 50, color: Colors.red),
Expanded(
child: Container(height: 50, color: Colors.green),
),
Flexible(
fit: FlexFit.loose,
child: Container(width: 30, height: 50, color: Colors.blue),
),
],
)
六、实战应用示例
1. 响应式布局
dart
class ResponsiveLayout extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
// 大屏幕:水平布局
return Row(
children: [
// 侧边栏
Container(
width: 200,
color: Colors.grey[200],
child: ListView(
children: List.generate(10, (i) => ListTile(title: Text('Item $i'))),
),
),
// 主内容
Expanded(
child: Container(
color: Colors.white,
child: Center(child: Text('Main Content')),
),
),
],
);
} else {
// 小屏幕:垂直布局
return Column(
children: [
// 顶部导航
Container(
height: 56,
color: Colors.grey[200],
child: Center(child: Text('Mobile Navigation')),
),
// 主内容
Expanded(
child: Container(
color: Colors.white,
child: ListView(
children: List.generate(20, (i) => ListTile(title: Text('Item $i'))),
),
),
),
],
);
}
},
);
}
}
2. 表格布局
dart
Column(
children: [
// 表头
Row(
children: [
Expanded(flex: 2, child: TableHeader('Name')),
Expanded(flex: 1, child: TableHeader('Age')),
Expanded(flex: 1, child: TableHeader('Score')),
],
),
Divider(height: 1),
// 表格内容
Expanded(
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return TableRowItem(index: index);
},
),
),
],
)
class TableHeader extends StatelessWidget {
final String title;
TableHeader(this.title);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(12),
color: Colors.grey[100],
child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
);
}
}
class TableRowItem extends StatelessWidget {
final int index;
TableRowItem({required this.index});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.all(12),
child: Text('User $index'),
),
),
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(12),
child: Text('${20 + index}'),
),
),
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(12),
child: Text('${80 + index}'),
),
),
],
);
}
}
3. 流式标签布局
dart
class TagFlowLayout extends StatefulWidget {
@override
_TagFlowLayoutState createState() => _TagFlowLayoutState();
}
class _TagFlowLayoutState extends State<TagFlowLayout> {
final tags = [
'Flutter', 'Dart', 'Mobile', 'Web', 'Desktop', 'iOS',
'Android', 'UI/UX', 'Animation', 'Firebase', 'Provider', 'Riverpod'
];
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 8.0,// 水平间距
runSpacing: 8.0,// 垂直间距
children: tags.map((tag) {
return Chip(
label: Text(tag),
avatar: CircleAvatar(
child: Text(tag[0]),
),
onDeleted: () {
setState(() {
tags.remove(tag);
});
},
);
}).toList(),
);
}
}
七、常见问题与解决方案
1. RenderFlex overflowed 错误
dart
// ❌ 错误:Row中内容超出屏幕
Row(
children: [
Container(width: 400, color: Colors.red),
Container(width: 400, color: Colors.blue),
],
)
// ✅ 解决方案1:使用SingleChildScrollView包裹
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [/* 宽组件 */],
),
)
// ✅ 解决方案2:使用Expanded
Row(
children: [
Expanded(child: Container(color: Colors.red)),
Expanded(child: Container(color: Colors.blue)),
],
)
2. 嵌套弹性布局问题
dart
// ❌ 错误:无限高度问题
Column(
children: [
Expanded(// Column内部使用Expanded
child: Container(color: Colors.red),
),
],
)
// ✅ 解决方案:确保父级有明确高度
Container(
height: 200,// 指定高度
child: Column(
children: [
Expanded(child: Container(color: Colors.red)),
],
),
)
3. Column/Row 内部滚动
dart
// 使用Expanded包裹ListView/GridView
Column(
children: [
Container(height: 100, color: Colors.red),
Expanded(// 让ListView占用剩余空间
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
),
),
],
)
八、最佳实践
- 选择合适的组件:
- 单行/单列布局:使用
Row/Column - 动态方向布局:使用
Flex - 需要填满空间:使用
Expanded - 灵活空间占用:使用
Flexible
- 性能优化:
dart
// 使用const减少重建
Column(
children: const [
SizedBox(height: 10),
Text('Title'),
Divider(),
],
)
- 响应式设计:
dart
LayoutBuilder(
builder: (context, constraints) {
return constraints.maxWidth > 600
? Row(children: [/* 水平布局 */])
: Column(children: [/* 垂直布局 */]);
},
)
- 组合使用:
dart
Column(
children: [
// 固定高度的Header
Container(height: 60, child: Header()),
// 可滚动的中间内容
Expanded(
child: ListView(
children: [/* 内容项 */],
),
),
// 固定高度的Footer
Container(height: 60, child: Footer()),
],
)
弹性布局是 Flutter 中最常用的布局方式,掌握好这些组件的使用,可以轻松构建各种复杂的界面布局。记住多实践,理解主轴和交叉轴的概念,这是掌握弹性布局的关键。