Skip to Content
Flutter📐 Layouts🎁 Wrap Widget

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)
endCuối dòng
centerGiữa dòng
spaceBetweenKhoảng cách đều, không có ở đầu/cuối
spaceAroundKhoảng cách đều, nửa ở đầu/cuối
spaceEvenlyKhoả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(), )
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

PropertyMô tả
spacingKhoảng cách ngang
runSpacingKhoảng cách giữa các dòng
alignmentCăn chỉnh trên mỗi dòng
runAlignmentCăn chỉnh các dòng
crossAxisAlignmentCăn chỉnh theo cross axis
directionHorizontal hoặc Vertical

Use cases phổ biến:

  • Tags / Chips
  • Filter buttons
  • Skills / Badges
  • Action buttons
  • Thumbnail galleries
Last updated on