Understanding Constraints trong Flutter
1. Nguyên tắc cốt lõi
Flutter layout tuân theo quy tắc đơn giản nhưng rất quan trọng:
“Constraints go down. Sizes go up. Parent sets position.”
(Constraints đi xuống. Kích thước đi lên. Parent đặt vị trí.)
2. Constraints là gì?
Constraints định nghĩa kích thước tối thiểu và tối đa mà một widget có thể có:
BoxConstraints(
minWidth: 0,
maxWidth: 300,
minHeight: 0,
maxHeight: 200,
)3. Quy trình Layout
┌─────────────────────────────────────────┐
│ 1. Parent gửi constraints xuống child │
│ "Bạn có thể rộng 0-300, cao 0-200" │
└───────────────────┬─────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 2. Child quyết định kích thước của nó │
│ "Tôi sẽ rộng 150, cao 100" │
└───────────────────┬─────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 3. Parent đặt vị trí cho child │
│ "Bạn sẽ ở vị trí (75, 50)" │
└─────────────────────────────────────────┘4. Tight vs Loose Constraints
Tight Constraints
Min = Max, widget buộc phải có kích thước cụ thể:
BoxConstraints.tight(Size(100, 100))
// minWidth = maxWidth = 100
// minHeight = maxHeight = 100Loose Constraints
Min = 0, widget có thể chọn bất kỳ kích thước nào từ 0 đến max:
BoxConstraints.loose(Size(100, 100))
// minWidth = 0, maxWidth = 100
// minHeight = 0, maxHeight = 1005. Unbounded Constraints
Khi maxWidth/maxHeight là vô hạn:
// Trong ListView, height là unbounded
// Trong Row, width là unbounded (cho children)⚠️ Đây là nguyên nhân gây ra nhiều lỗi layout!
6. Lỗi thường gặp
”RenderBox was not laid out"
// ❌ Lỗi: Column trong Row, cả hai đều cố gắng expand
Row(
children: [
Column(
children: [Text('Item 1'), Text('Item 2')],
),
],
)
// ✅ Fix: Wrap với Expanded hoặc set kích thước
Row(
children: [
Expanded(
child: Column(
children: [Text('Item 1'), Text('Item 2')],
),
),
],
)"Vertical viewport was given unbounded height”
// ❌ Lỗi: ListView trong Column (cả hai expand vô hạn theo height)
Column(
children: [
ListView(...),
],
)
// ✅ Fix 1: Wrap ListView với Expanded
Column(
children: [
Expanded(
child: ListView(...),
),
],
)
// ✅ Fix 2: Hoặc set shrinkWrap
Column(
children: [
ListView(
shrinkWrap: true, // ListView co theo nội dung
),
],
)7. LayoutBuilder
Đọc constraints trong runtime:
LayoutBuilder(
builder: (context, constraints) {
print('Max width: ${constraints.maxWidth}');
print('Max height: ${constraints.maxHeight}');
if (constraints.maxWidth > 600) {
return WideLayout();
} else {
return NarrowLayout();
}
},
)8. IntrinsicHeight / IntrinsicWidth
Cho children của Row/Column có cùng kích thước:
// Tất cả children trong Row có cùng height
IntrinsicHeight(
child: Row(
children: [
Container(color: Colors.red, width: 50, height: 100),
Container(color: Colors.green, width: 50), // Sẽ có height = 100
Container(color: Colors.blue, width: 50), // Sẽ có height = 100
],
),
)⚠️ IntrinsicHeight/Width tốn performance, dùng hạn chế!
9. Một số widgets và constraints
| Widget | Behavior |
|---|---|
Container | Nếu không có child, expand full. Có child thì co theo child |
SizedBox | Đặt constraints cụ thể |
ConstrainedBox | Thêm constraints |
UnconstrainedBox | Bỏ constraints (child có thể overflow) |
FractionallySizedBox | Kích thước theo % của parent |
10. Debug Layout
// Xem constraints trong debug
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
debugPrint('Constraints: $constraints');
return MyWidget();
},
);
}Hoặc sử dụng Flutter DevTools → Layout Explorer.
📝 Tóm tắt
| Khái niệm | Mô tả |
|---|---|
| Constraints | Giới hạn min/max của kích thước |
| Tight | min = max (kích thước cố định) |
| Loose | min = 0 (có thể nhỏ hơn) |
| Unbounded | max = infinity (nguy hiểm!) |
Quy tắc vàng:
- Constraints đi từ parent xuống child
- Sizes đi từ child lên parent
- Parent quyết định position của child
Last updated on