Skip to Content

Stack Widget trong Flutter

1. Giới thiệu

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 ], )

Nguyên lý xếp chồng (Layering)

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)

2. Alignment

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), ], )

3. Positioned Widget

Đị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), ), ], )

Positioned properties

PropertyMô tả
topKhoảng cách từ top
bottomKhoảng cách từ bottom
leftKhoảng cách từ left
rightKhoảng cách từ right
widthChiều rộng cố định
heightChiều cao cố định

4. Positioned.fill

Kéo dãn child full Stack:

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

5. fit Property

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

6. clipBehavior

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), ), ], )

7. Ví dụ thực tế

Badge trên Icon

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), ), ), ), ], )

Image với Overlay

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), ), ), ), ], )

Floating Action Button Style

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), ), ), ], )

Profile Avatar với Status

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), ), ), ), ], )

8. IndexedStack

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.


📝 Tóm tắt

Widget/PropertyMục đích
StackXếp chồng widgets
PositionedĐịnh vị chính xác trong Stack
alignmentCăn chỉnh mặc định cho children
fitCách sizing children
clipBehaviorCho phép/không cho tràn
IndexedStackHiển thị một child theo index
Last updated on