Wrap Widget trong Flutter
1. Wrap cơ bản
Wrap tự động xuống dòng khi children vượt quá không gian:
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')),
],
)2. So sánh Row vs Wrap
// ❌ Row: Overflow khi không đủ chỗ
Row(
children: [Chip(label: Text('Long tag 1')), Chip(label: Text('Long tag 2')), ...],
)
// ✅ Wrap: Tự động xuống dòng
Wrap(
children: [Chip(label: Text('Long tag 1')), Chip(label: Text('Long tag 2')), ...],
)3. Spacing
Wrap(
spacing: 8, // Khoảng cách ngang giữa children
runSpacing: 8, // Khoảng cách giữa các dòng
children: [
Chip(label: Text('Tag 1')),
Chip(label: Text('Tag 2')),
Chip(label: Text('Tag 3')),
],
)4. Alignment
alignment (trên mỗi dòng)
Wrap(
alignment: WrapAlignment.center, // Căn giữa các items trên mỗi dòng
children: [...],
)| Giá trị | Mô tả |
|---|---|
start | Đầu dòng (mặc định) |
end | Cuối dòng |
center | Giữa dòng |
spaceBetween | Khoảng cách đều, không có ở đầu/cuối |
spaceAround | Khoảng cách đều, nửa ở đầu/cuối |
spaceEvenly | Khoảng cách đều hoàn toàn |
runAlignment (giữa các dòng)
Wrap(
runAlignment: WrapAlignment.center, // Căn các dòng theo chiều dọc
children: [...],
)crossAxisAlignment
Wrap(
crossAxisAlignment: WrapCrossAlignment.center, // Căn children theo cross axis
children: [...],
)5. Direction
Wrap(
direction: Axis.vertical, // Wrap theo chiều dọc
children: [...],
)6. Ví dụ thực tế
Tags/Chips
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Chip(
label: Text('Flutter'),
onDeleted: () {},
),
Chip(
label: Text('Dart'),
onDeleted: () {},
),
Chip(
label: Text('Mobile'),
onDeleted: () {},
),
Chip(
label: Text('Cross-platform'),
onDeleted: () {},
),
],
)Filter Buttons
Wrap(
spacing: 8,
runSpacing: 8,
children: [
FilterChip(
label: Text('All'),
selected: true,
onSelected: (v) {},
),
FilterChip(
label: Text('Active'),
selected: false,
onSelected: (v) {},
),
FilterChip(
label: Text('Completed'),
selected: false,
onSelected: (v) {},
),
],
)Action Buttons
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.share),
label: Text('Share'),
),
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.download),
label: Text('Download'),
),
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.print),
label: Text('Print'),
),
],
)Skills/Badges
Wrap(
spacing: 8,
runSpacing: 8,
children: skills.map((skill) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(20),
),
child: Text(skill),
);
}).toList(),
)Image Gallery Thumbnails
Wrap(
spacing: 4,
runSpacing: 4,
children: images.map((url) {
return ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.network(
url,
width: 80,
height: 80,
fit: BoxFit.cover,
),
);
}).toList(),
)7. Wrap vs Flow
Flow cho phép vị trí tùy chỉnh hoàn toàn nhưng phức tạp hơn:
// Wrap: Đơn giản, auto wrap
Wrap(children: [...])
// Flow: Custom positioning, cần FlowDelegate
Flow(
delegate: MyFlowDelegate(),
children: [...],
)💡 Dùng Wrap cho hầu hết trường hợp, Flow chỉ khi cần layout đặc biệt.
📝 Tóm tắt
| Property | Mô tả |
|---|---|
spacing | Khoảng cách ngang |
runSpacing | Khoảng cách giữa các dòng |
alignment | Căn chỉnh trên mỗi dòng |
runAlignment | Căn chỉnh các dòng |
crossAxisAlignment | Căn chỉnh theo cross axis |
direction | Horizontal hoặc Vertical |
Use cases phổ biến:
- Tags / Chips
- Filter buttons
- Skills / Badges
- Action buttons
- Thumbnail galleries
Last updated on