🎁 Wrap Widget
1. Wrap cơ bản
Phần tiêu đề “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
Phần tiêu đề “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òngWrap( children: [Chip(label: Text('Long tag 1')), Chip(label: Text('Long tag 2')), ...],)3. Spacing
Phần tiêu đề “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
Phần tiêu đề “4. Alignment”alignment (trên mỗi dòng)
Phần tiêu đề “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)
Phần tiêu đề “runAlignment (giữa các dòng)”Wrap( runAlignment: WrapAlignment.center, // Căn các dòng theo chiều dọc children: [...],)crossAxisAlignment
Phần tiêu đề “crossAxisAlignment”Wrap( crossAxisAlignment: WrapCrossAlignment.center, // Căn children theo cross axis children: [...],)5. Direction
Phần tiêu đề “5. Direction”Wrap( direction: Axis.vertical, // Wrap theo chiều dọc children: [...],)6. Ví dụ thực tế
Phần tiêu đề “6. Ví dụ thực tế”Tags/Chips
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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 wrapWrap(children: [...])
// Flow: Custom positioning, cần FlowDelegateFlow( 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
Phần tiêu đề “📝 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