Skip to Content
Flutter📐 Layouts📐 Constraints

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 = 100

Loose 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 = 100

5. 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

WidgetBehavior
ContainerNếu không có child, expand full. Có child thì co theo child
SizedBoxĐặt constraints cụ thể
ConstrainedBoxThêm constraints
UnconstrainedBoxBỏ constraints (child có thể overflow)
FractionallySizedBoxKí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ệmMô tả
ConstraintsGiới hạn min/max của kích thước
Tightmin = max (kích thước cố định)
Loosemin = 0 (có thể nhỏ hơn)
Unboundedmax = infinity (nguy hiểm!)

Quy tắc vàng:

  1. Constraints đi từ parent xuống child
  2. Sizes đi từ child lên parent
  3. Parent quyết định position của child
Last updated on