Bố cục là kỹ năng dùng nhiều nhất trong Flutter. Bài này dạy nguyên lý ràng buộc kích thước và các widget bố cục: Row, Column, Stack, Expanded, Padding, SizedBox, cùng cách làm giao diện co giãn.
- Hiểu nguyên tắc “constraints go down, sizes go up”
- Sắp xếp bằng Row, Column, Stack
- Chia không gian bằng Expanded, Flexible, Spacer
- Làm giao diện responsive với LayoutBuilder
- Constraints
- Giới hạn kích thước tối thiểu/tối đa cha truyền cho con.
- Main axis
- Trục chính: ngang với Row, dọc với Column.
- Overflow
- Nội dung vượt khung, hiện sọc vàng đen.
- Breakpoint
- Mốc chiều rộng để đổi bố cục.
Khái niệm và lý thuyết
Quy tắc vàng của bố cục Flutter: ràng buộc đi xuống, kích thước đi lên, cha quyết định vị trí. Widget cha truyền giới hạn cho con; con chọn kích thước trong giới hạn đó và báo lại; cha đặt con vào vị trí. Hiểu quy tắc này giúp giải thích hầu hết lỗi bố cục như “RenderFlex overflowed” hay “unbounded height”.
| Widget | Vai trò |
|---|---|
| Row / Column | Xếp con theo hàng ngang / cột dọc |
| Stack + Positioned | Chồng lớp, đặt vị trí tuyệt đối |
| Expanded / Flexible | Chiếm phần không gian còn lại theo tỉ lệ flex |
| Padding, SizedBox, Spacer | Khoảng đệm, khoảng cách |
| Wrap | Tự xuống dòng khi hết chỗ |
Row và Column
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Khóa học Flutter', style: TextStyle(fontSize: 22)),
const SizedBox(height: 8),
Row(
children: [
const Icon(Icons.star, color: Colors.amber),
const SizedBox(width: 4),
const Text('4.9'),
const Spacer(),
FilledButton(onPressed: () {}, child: const Text('Học ngay')),
],
),
],
)| Thuộc tính | Ý nghĩa |
|---|---|
| mainAxisAlignment | Căn theo trục chính: start, center, spaceBetween... |
| crossAxisAlignment | Căn theo trục phụ |
| mainAxisSize | min: co theo nội dung; max: chiếm hết |
Expanded và Stack
Row(children: [
Expanded(flex: 2, child: Container(height: 60, color: Colors.green)),
Expanded(flex: 1, child: Container(height: 60, color: Colors.orange)),
]); // tỉ lệ 2:1
Stack(children: [
Image.network('https://picsum.photos/400/200'),
const Positioned(bottom: 8, left: 8,
child: Text('Ảnh bìa', style: TextStyle(color: Colors.white))),
]);Responsive
LayoutBuilder(builder: (context, c) {
if (c.maxWidth >= 840) return const BoCucMayTinh(); // 3 cột
if (c.maxWidth >= 600) return const BoCucMayTinhBang(); // 2 cột
return const BoCucDienThoai(); // 1 cột
});
// Kích thước màn hình: MediaQuery.sizeOf(context).widthDựng thẻ gồm ảnh (Stack có nhãn “Giảm 20%”), tên sản phẩm, hàng chứa giá và nút giỏ hàng dùng Spacer đẩy nút sang phải. Bọc thẻ trong Padding 12 và Card bo góc.
Bật Flutter Inspector trong DevTools và chọn “Show guidelines” để thấy khung, padding, trục của từng widget khi gỡ lỗi bố cục.
Đặt ListView trong Column mà không bọc Expanded sẽ gây lỗi “unbounded height” vì ListView muốn cao vô hạn.
- Dựng màn hình hồ sơ: ảnh tròn (CircleAvatar), tên, hàng 3 số liệu cách đều.
- Tạo thẻ sản phẩm như tình huống trên.
- Dùng Wrap hiển thị 10 thẻ kỹ năng (Chip).
- Làm lưới 1/2/3 cột theo chiều rộng bằng LayoutBuilder.
- Constraints đi xuống, sizes đi lên, cha đặt vị trí.
- Row/Column/Stack là ba bố cục chính.
- Expanded chia không gian; SizedBox/Spacer tạo khoảng cách.
- LayoutBuilder và MediaQuery cho giao diện co giãn.