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

📚 Stack Widget

Stack cho phép xếp chồng widgets lên nhau:

Stack(
children: [
Container(width: 200, height: 200, color: Colors.red), // Dưới cùng
Container(width: 150, height: 150, color: Colors.green), // Giữa
Container(width: 100, height: 100, color: Colors.blue), // Trên cùng
],
)

Stack vẽ các children theo thứ tự: First-in, Bottom-out (Cái nào vào trước nằm dưới cùng).

STACK VISUALIZATION
┌──────────────┐
│ Top (3) │ ← Last Child (Blue)
┌─┴────────────┐ │
│ Middle (2) │ │ ← Middle Child (Green)
┌─┴────────────┐ │ │
│ Bottom (1) │ │ │ ← First Child (Red)
└──────────────┘─┴─┘
(3) che khuất (2)
(2) che khuất (1)

Stack(
alignment: Alignment.center, // Căn tất cả children vào giữa
children: [
Container(width: 200, height: 200, color: Colors.grey),
Container(width: 100, height: 100, color: Colors.blue),
],
)

Định vị chính xác child trong Stack:

Stack(
children: [
Container(width: 200, height: 200, color: Colors.grey),
Positioned(
top: 10,
left: 10,
child: Container(width: 50, height: 50, color: Colors.red),
),
Positioned(
bottom: 10,
right: 10,
child: Container(width: 50, height: 50, color: Colors.blue),
),
],
)
Property Mô tả
top Khoảng cách từ top
bottom Khoảng cách từ bottom
left Khoảng cách từ left
right Khoảng cách từ right
width Chiều rộng cố định
height Chiều cao cố định

Kéo dãn child full Stack:

Stack(
children: [
Positioned.fill(
child: Container(color: Colors.grey),
),
Center(
child: Text('Centered'),
),
],
)

Stack(
fit: StackFit.expand, // Kéo dãn children không có Positioned
children: [...],
)
Giá trị Mô tả
loose Children có thể nhỏ hơn Stack (mặc định)
expand Kéo dãn non-positioned children full Stack
passthrough Giữ constraints từ parent

Stack(
clipBehavior: Clip.none, // Cho phép children tràn ra ngoài
children: [
Container(width: 100, height: 100, color: Colors.grey),
Positioned(
top: -20,
left: -20,
child: Container(width: 50, height: 50, color: Colors.red),
),
],
)

Stack(
clipBehavior: Clip.none,
children: [
Icon(Icons.notifications, size: 30),
Positioned(
top: -5,
right: -5,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text(
'3',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
),
],
)
Stack(
children: [
Image.network(
'https://picsum.photos/200',
width: 200,
height: 200,
fit: BoxFit.cover,
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(8),
color: Colors.black54,
child: Text(
'Image Title',
style: TextStyle(color: Colors.white),
),
),
),
],
)
Stack(
children: [
// Main content
ListView.builder(
itemCount: 20,
itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
),
// FAB positioned
Positioned(
bottom: 16,
right: 16,
child: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
),
],
)
Stack(
children: [
CircleAvatar(
radius: 40,
backgroundImage: NetworkImage('https://...'),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
),
),
],
)

Chỉ hiển thị một child tại một thời điểm:

IndexedStack(
index: currentIndex, // 0, 1, hoặc 2
children: [
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
],
)

Hữu ích cho tab switching mà muốn giữ state.


Widget/Property Mục đích
Stack Xếp chồng widgets
Positioned Định vị chính xác trong Stack
alignment Căn chỉnh mặc định cho children
fit Cách sizing children
clipBehavior Cho phép/không cho tràn
IndexedStack Hiển thị một child theo index