Flutter流式布局 #
一、Wrap #
1.1 基本用法 #
Wrap是一个可以自动换行的布局容器,当空间不足时会自动换行。
dart
Wrap({
Key? key,
Axis direction = Axis.horizontal,
WrapAlignment alignment = WrapAlignment.start,
double spacing = 0.0,
WrapAlignment runAlignment = WrapAlignment.start,
double runSpacing = 0.0,
WrapCrossAlignment crossAxisAlignment = WrapCrossAlignment.start,
TextDirection? textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
Clip clipBehavior = Clip.none,
List<Widget> children = const <Widget>[],
})
Wrap(
children: [
Chip(label: Text('Tag 1')),
Chip(label: Text('Tag 2')),
Chip(label: Text('Tag 3')),
Chip(label: Text('Tag 4')),
Chip(label: Text('Tag 5')),
],
)
1.2 direction属性 #
控制主轴方向:
dart
Wrap(
direction: Axis.horizontal,
children: [...],
)
Wrap(
direction: Axis.vertical,
children: [...],
)
1.3 spacing属性 #
主轴方向子Widget之间的间距:
dart
Wrap(
spacing: 8,
children: [
Chip(label: Text('Tag 1')),
Chip(label: Text('Tag 2')),
Chip(label: Text('Tag 3')),
],
)
1.4 runSpacing属性 #
交叉轴方向行/列之间的间距:
dart
Wrap(
spacing: 8,
runSpacing: 16,
children: [
Chip(label: Text('Tag 1')),
Chip(label: Text('Tag 2')),
Chip(label: Text('Tag 3')),
Chip(label: Text('Tag 4')),
Chip(label: Text('Tag 5')),
],
)
1.5 alignment属性 #
主轴方向对齐方式:
dart
Wrap(
alignment: WrapAlignment.center,
children: [...],
)
WrapAlignment.start
WrapAlignment.end
WrapAlignment.center
WrapAlignment.spaceBetween
WrapAlignment.spaceAround
WrapAlignment.spaceEvenly
1.6 runAlignment属性 #
交叉轴方向对齐方式:
dart
Wrap(
runAlignment: WrapAlignment.center,
children: [...],
)
1.7 crossAxisAlignment属性 #
交叉轴对齐方式:
dart
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 100, color: Colors.green),
Container(width: 50, height: 75, color: Colors.blue),
],
)
WrapCrossAlignment.start
WrapCrossAlignment.end
WrapCrossAlignment.center
二、Wrap实战示例 #
2.1 标签云 #
dart
Wrap(
spacing: 8,
runSpacing: 8,
children: [
'Flutter',
'Dart',
'Android',
'iOS',
'Web',
'Desktop',
'Mobile',
'Cross-platform',
].map((tag) => Chip(
label: Text(tag),
backgroundColor: Colors.blue.shade100,
)).toList(),
)
2.2 可删除标签 #
dart
class TagList extends StatefulWidget {
@override
State<TagList> createState() => _TagListState();
}
class _TagListState extends State<TagList> {
final List<String> _tags = ['Flutter', 'Dart', 'Android', 'iOS'];
void _removeTag(String tag) {
setState(() {
_tags.remove(tag);
});
}
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 8,
runSpacing: 8,
children: _tags.map((tag) => Chip(
label: Text(tag),
deleteIcon: Icon(Icons.close, size: 16),
onDeleted: () => _removeTag(tag),
)).toList(),
);
}
}
2.3 图片网格 #
dart
Wrap(
spacing: 4,
runSpacing: 4,
children: List.generate(9, (index) {
return Container(
width: (MediaQuery.of(context).size.width - 16) / 3 - 4,
height: (MediaQuery.of(context).size.width - 16) / 3 - 4,
color: Colors.primaries[index % Colors.primaries.length],
child: Center(child: Text('${index + 1}')),
);
}),
)
2.4 搜索历史 #
dart
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('搜索历史', style: TextStyle(fontWeight: FontWeight.bold)),
TextButton(
onPressed: () {},
child: Text('清空'),
),
],
),
SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
'Flutter教程',
'Dart语言',
'Widget详解',
'状态管理',
].map((keyword) => ActionChip(
label: Text(keyword),
onPressed: () {
print('Search: $keyword');
},
)).toList(),
),
],
)
三、Flow #
3.1 基本用法 #
Flow是一个更底层的流式布局,需要自定义布局逻辑。
dart
Flow({
Key? key,
required FlowDelegate delegate,
List<Widget> children = const <Widget>[],
Clip clipBehavior = Clip.hardEdge,
})
class MyFlowDelegate extends FlowDelegate {
@override
void paintChildren(FlowPaintingContext context) {
double x = 0;
double y = 0;
for (int i = 0; i < context.childCount; i++) {
var child = context.getChildSize(i);
if (x + child!.width > context.size.width) {
x = 0;
y += child.height;
}
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
x += child.width;
}
}
@override
bool shouldRepaint(covariant FlowDelegate oldDelegate) {
return false;
}
}
Flow(
delegate: MyFlowDelegate(),
children: [
Container(width: 80, height: 80, color: Colors.red),
Container(width: 100, height: 60, color: Colors.green),
Container(width: 60, height: 100, color: Colors.blue),
],
)
3.2 自定义布局示例 #
dart
class CircleFlowDelegate extends FlowDelegate {
@override
void paintChildren(FlowPaintingContext context) {
final centerX = context.size.width / 2;
final centerY = context.size.height / 2;
final radius = 100.0;
for (int i = 0; i < context.childCount; i++) {
final angle = (2 * 3.14159 * i) / context.childCount;
final x = centerX + radius * cos(angle) - context.getChildSize(i)!.width / 2;
final y = centerY + radius * sin(angle) - context.getChildSize(i)!.height / 2;
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
}
}
@override
bool shouldRepaint(covariant FlowDelegate oldDelegate) => false;
}
Flow(
delegate: CircleFlowDelegate(),
children: List.generate(6, (index) =>
Container(
width: 50,
height: 50,
color: Colors.primaries[index % Colors.primaries.length],
child: Center(child: Text('${index + 1}')),
),
),
)
四、Wrap vs Flow #
| 特性 | Wrap | Flow |
|---|---|---|
| 易用性 | 简单 | 复杂 |
| 灵活性 | 一般 | 高 |
| 性能 | 较好 | 更好 |
| 自定义 | 有限 | 完全自定义 |
五、总结 #
5.1 核心属性 #
| 属性 | 说明 |
|---|---|
| direction | 主轴方向 |
| spacing | 主轴间距 |
| runSpacing | 交叉轴间距 |
| alignment | 主轴对齐 |
| runAlignment | 交叉轴对齐 |
5.2 下一步 #
掌握了流式布局后,让我们学习 层叠布局!
最后更新:2026-03-28