Bỏ qua để đến nội dung

↔️ Row & Column

RowColumn là hai widget layout cơ bản nhất:

  • Row → Sắp xếp children theo chiều ngang
  • Column → Sắp xếp children theo chiều dọc
// Hàng ngang
Row(
children: [Widget1(), Widget2(), Widget3()],
)
// Cột dọc
Column(
children: [Widget1(), Widget2(), Widget3()],
)

Hiểu về trục chính (Main) và trục phụ (Cross) là chìa khóa để mastering layout:

ROW (Hàng Ngang) COLUMN (Cột Dọc)
Cross Axis (Vertical) Main Axis (Vertical)
↑ ↑
│ │
Main ───┼───▶ Axis (Horizontal) Cross ──┼──▶ Axis (Horizontal)
│ │
↓ ↓
  • Row:
    • Main Axis: Nằm ngang (→)
    • Cross Axis: Nằm dọc (↓)
  • Column:
    • Main Axis: Nằm dọc (↓)
    • Cross Axis: Nằm ngang (→)

Row(
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)

Column(
children: [
Container(width: 50, height: 50, color: Colors.red),
Container(width: 50, height: 50, color: Colors.green),
Container(width: 50, height: 50, color: Colors.blue),
],
)

Căn chỉnh theo trục chính (ngang cho Row, dọc cho Column):

Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [...],
)
Giá trị Mô tả
start Đầu trục (mặc định)
end Cuối trục
center Giữa trục
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

Căn chỉnh theo trục phụ (dọc cho Row, ngang cho Column):

Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [...],
)
Giá trị Mô tả
start Đầu trục phụ
end Cuối trục phụ
center Giữa trục phụ (mặc định)
stretch Kéo dãn full chiều
baseline Căn theo baseline (chỉ Row)

Row(
mainAxisSize: MainAxisSize.min, // Co lại vừa children
children: [...],
)
Giá trị Mô tả
max Chiếm hết không gian có sẵn (mặc định)
min Co lại vừa children

Row(
children: [
Text('Item 1'),
SizedBox(width: 16), // Khoảng cách ngang
Text('Item 2'),
SizedBox(width: 16),
Text('Item 3'),
],
)
Column(
children: [
Text('Item 1'),
SizedBox(height: 16), // Khoảng cách dọc
Text('Item 2'),
],
)

Chiếm hết không gian còn lại:

Row(
children: [
Container(width: 50, height: 50, color: Colors.red),
Expanded(
child: Container(height: 50, color: Colors.green), // Chiếm hết
),
Container(width: 50, height: 50, color: Colors.blue),
],
)
Row(
children: [
Expanded(
flex: 1, // 1 phần
child: Container(height: 50, color: Colors.red),
),
Expanded(
flex: 2, // 2 phần
child: Container(height: 50, color: Colors.green),
),
],
)

Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.menu),
Text('Title', style: TextStyle(fontSize: 18)),
Icon(Icons.search),
],
)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Username', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
TextField(),
SizedBox(height: 16),
Text('Password', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
TextField(obscureText: true),
SizedBox(height: 24),
ElevatedButton(
onPressed: () {},
child: Text('Login'),
),
],
)
Row(
children: [
CircleAvatar(radius: 30, backgroundImage: NetworkImage('...')),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('John Doe', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('john@email.com', style: TextStyle(color: Colors.grey)),
],
),
),
IconButton(icon: Icon(Icons.edit), onPressed: () {}),
],
)

// ❌ Lỗi overflow
Row(
children: [
Text('Very very very very long text...'),
],
)
// ✅ Fix với Expanded hoặc Flexible
Row(
children: [
Expanded(
child: Text('Very very long text...', overflow: TextOverflow.ellipsis),
),
],
)

Widget/Property Mục đích
Row Layout ngang
Column Layout dọc
mainAxisAlignment Căn chỉnh trục chính
crossAxisAlignment Căn chỉnh trục phụ
mainAxisSize Kích thước trục chính
Expanded Chiếm không gian còn lại
SizedBox Tạo khoảng cách