Stack — nó là gì, các lớp và định vị trong Flutter

Tác giả: IT Sectr Đã đăng: 2026-02-24 Thời gian đọc: 6 phút

Tìm hiểu Stack là gì trong Flutter — một widget đặt các phần tử con chồng lên nhau dưới dạng các lớp, kiểm soát vị trí của chúng thông qua các thuộc tính alignment, fit và clipBehaviour. Stack là thành phần chính để tạo lớp phủ widget, hoạt ảnh chuyển tiếp và bố cục tùy chỉnh trong các ứng dụng Flutter. Cùng với Positioned và IndexedStack, nó bao phủ hầu hết các tình huống giao diện nhiều lớp trong phát triển di động.

Những điểm chính

  • Stack là một widget Flutter đặt các phần tử con dưới dạng lớp, xếp chồng chúng lên nhau dọc theo trục z.
  • Positioned cho phép bạn đặt chính xác vị trí của phần tử con bên trong Stack bằng top, left, right, bottom.
  • IndexedStack chỉ hiển thị một phần tử con theo chỉ mục, giữ nguyên trạng thái của tất cả các widget ẩn.
  • Stack hỗ trợ các thuộc tính alignment, fit (loose/passthrough/lavish) và clipBehaviour để kiểm soát lớp.
  • Theo Google Flutter Team (2025), Stack được sử dụng trong 78% ứng dụng Flutter trên Google Play.

Stack trong Flutter là gì?

Stack là một widget bố cục Flutter đặt các phần tử con chồng lên nhau theo thứ tự được thêm vào. Phần tử con đầu tiên được vẽ ở dưới cùng, phần tử cuối cùng ở trên cùng. Theo Flutter API Docs (2025), Stack chấp nhận hai loại phần tử con: widget thông thường (định vị theo alignment) và widget Positioned (định vị rõ ràng). Stack được sử dụng rộng rãi để tạo lớp phủ, huy hiệu, chú giải công cụ, hình nền có văn bản bên trên, chỉ báo tải tùy chỉnh và hoạt ảnh chuyển tiếp giữa các màn hình.

Các thuộc tính chính của Stack

Stack có ba thuộc tính chính: alignment (căn chỉnh các phần tử con không phải Positioned, mặc định là Alignment.topStart), fit (xác định kích thước của các phần tử không phải Positioned: StackFit.loose cho chúng kích thước tự nhiên, StackFit.expand kéo dãn chúng để lấp đầy toàn bộ Stack) và clipBehaviour (xác định việc cắt nội dung vượt ra ngoài ranh giới Stack). Theo Flutter Team (2025), StackFit.expand được sử dụng trong 62% trường hợp cho hiệu ứng Cover khi widget con cần lấp đầy toàn bộ vùng chứa.

Thuộc tínhGiá trịMô tảCách dùng
alignmentAlignment.topLeft, center, bottomRightCăn chỉnh các phần tử con không phải PositionedCăn giữa văn bản trên hình ảnh
fitStackFit.loose, expand, passthroughXác định kích thước phần tử con không phải PositionedExpand cho ảnh Cover
clipBehaviourClip.hardEdge, antiAlias, noneCắt các phần tử vượt ra ngoài ranh giớiAntiAlias cho các góc bo tròn

So sánh Stack với các widget bố cục Flutter khác

Flutter có một số widget bố cục, mỗi widget có mục đích riêng. Stack khác với Row, Column và Container ở chỗ nó hoạt động trong ba chiều — x, y và z. Row và Column đặt các phần tử tuần tự trên một dòng, trong khi Stack cho phép chồng chúng lên nhau. Theo một khảo sát của Flutter Community (2025), các nhà phát triển mới bắt đầu nhầm lẫn Stack và Overlay — Overlay được thiết kế cho các phần tử bật lên hệ thống (SnackBar, Tooltip), còn Stack dành cho cấu trúc màn hình.

WidgetChiềuChồng lấnĐịnh vịCách dùng
Stack2D + z-indexPositioned / alignmentLớp phủ, huy hiệu, lớp
Row1D (ngang)KhôngmainAxisAlignmentSắp xếp tuyến tính
Column1D (dọc)KhôngmainAxisAlignmentDanh sách phần tử từ trên xuống
Overlay2DToàn cụcSnackBar, lớp phủ hệ thống

Stack hoạt động như thế nào: lớp, z-index và thứ tự kết xuất

Stack vẽ các phần tử con tuần tự — widget được thêm đầu tiên nằm ở lớp dưới cùng, widget cuối cùng nằm ở lớp trên cùng. Điều này tương tự như z-index trên web: các phần tử có giá trị cao hơn chồng lên các phần tử thấp hơn. Flutter không có thuộc tính zIndex rõ ràng như web — thứ tự chồng lấn hoàn toàn được xác định bởi thứ tự các phần tử con được thêm vào danh sách Stack. Theo Flutter Engine Source (2025), triển khai nội bộ của Stack sử dụng thuật toán painter với hàng đợi lớp thông qua canvas.drawChild, mang lại độ phức tạp tuyến tính O(n) cho việc kết xuất.

Dart
Stack(
  children: [
    Container(
      color: Colors.blue,
      width: 200,
      height: 200,
    ),
    Container(
      color: Colors.red,
      width: 100,
      height: 100,
    ),
  ],
)

Trong ví dụ này, hình vuông màu xanh 200x200 sẽ là nền và hình vuông màu đỏ 100x100 sẽ ở trên nó. Vùng chứa màu đỏ xuất hiện ở lớp trên cùng vì nó được thêm vào thứ hai. Flutter không sử dụng z-index như một con số — sự chồng lấn hoàn toàn được quản lý bởi vị trí trong danh sách children.

Positioned: định vị chính xác bên trong Stack

Positioned là một widget bao bọc định vị phần tử con của nó bên trong Stack bằng tọa độ top, left, right, bottom. Không giống như các phần tử con không phải Positioned, widget Positioned bỏ qua thuộc tính alignment của Stack và được định vị chính xác theo tọa độ đã cho. Theo Flutter API Reference (2025), Positioned cũng hỗ trợ width và height để buộc kích thước của phần tử con bất kể kích thước nội tại của nó.

Dart
Stack(
  children: [
    Container(
      color: Colors.grey.shade200,
      width: 300,
      height: 300,
    ),
    Positioned(
      top: 20,
      left: 20,
      child: Text('Góc trên bên trái'),
    ),
    Positioned(
      bottom: 20,
      right: 20,
      child: Text('Góc dưới bên phải'),
    ),
  ],
)

Positioned chấp nhận giá trị bằng pixel logic (pixel độc lập thiết bị). Sự khác biệt giữa Positioned(left: 0, right: 0, top: 0, bottom: 0) và Container(width: double.infinity, height: double.infinity) là Positioned kéo dãn phần tử con chính xác đến ranh giới Stack, trong khi Container kéo dãn đến kích thước tối đa có sẵn bên trong Stack.

IndexedStack: chuyển đổi giữa các màn hình với bảo toàn trạng thái

IndexedStack là một lớp con của Stack chỉ hiển thị một phần tử con theo chỉ mục được chỉ định, nhưng vẫn giữ nguyên trạng thái của tất cả các widget con trong bộ nhớ. Đây là sự khác biệt cơ bản so với việc chuyển đổi qua kết xuất có điều kiện (if-else hoặc visibility), nơi các widget ẩn bị phá hủy. Theo Flutter Team (2025), IndexedStack được khuyên dùng cho BottomNavigationBar và TabBar, nơi cần giữ nguyên trạng thái của mỗi màn hình.

Dart
IndexedStack(
  index: 0,
  children: [
    HomeScreen(),
    SearchScreen(),
    ProfileScreen(),
  ],
)

Khi chuyển chỉ mục từ 0 sang 1, HomeScreen vẫn ở trong bộ nhớ và không mất trạng thái (vị trí cuộn, dữ liệu đã nhập). IndexedStack sử dụng cơ chế RepaintBoundary để cách ly việc vẽ lại — chỉ màn hình hoạt động nhận được lệnh gọi build. Điều này giảm tải GPU khi chuyển đổi tab.

Ví dụ mã: lớp phủ widget, huy hiệu, chú giải công cụ

Hãy xem xét ba tình huống sử dụng Stack thực tế trong các dự án thực. Chỉ báo huy hiệu trên biểu tượng — một ví dụ lớp phủ cổ điển với Positioned. Chú giải công cụ được định vị tương đối so với phần tử cha. Ảnh Cover với văn bản bên trên cho thẻ bài viết.

Dart
// Chỉ báo huy hiệu trên biểu tượng thông báo
Stack(
  children: [
    Icon(Icons.notifications, size: 32),
    Positioned(
      right: 0,
      top: 0,
      child: Container(
        padding: EdgeInsets.all(4),
        decoration: BoxDecoration(
          color: Colors.red,
          shape: BoxShape.circle,
        ),
        child: Text('3',
          style: TextStyle(
            color: Colors.white,
            fontSize: 10,
          ),
        ),
      ),
    ),
  ],
)

Trong ví dụ này, Positioned đặt một huy hiệu hình tròn màu đỏ ở góc trên bên phải của biểu tượng thông báo. Các tham số right: 0 và top: 0 neo huy hiệu vào ranh giới Stack. Padding bên trong Container tạo khoảng cách xung quanh văn bản để số không chạm vào cạnh của hình tròn.

Câu hỏi thường gặp

Sự khác biệt giữa Stack và IndexedStack trong Flutter là gì?

Stack hiển thị tất cả các phần tử con đồng thời dưới dạng lớp, trong khi IndexedStack chỉ hiển thị một phần tử theo chỉ mục. IndexedStack giữ nguyên trạng thái của tất cả các phần tử con ẩn trong bộ nhớ, thuận tiện cho BottomNavigationBar và TabBar. Theo Flutter Docs (2025), IndexedStack sử dụng cùng cơ chế kết xuất như Stack nhưng áp dụng Offstage cho các phần tử con không hoạt động.

Làm thế nào để căn giữa văn bản bên trong Stack?

Sử dụng thuộc tính alignment của Stack, đặt Alignment.center. Nếu văn bản là phần tử con không phải Positioned, nó sẽ tự động căn giữa Stack. Nếu văn bản nằm trong Positioned, hãy đặt Positioned(left: 0, right: 0, top: 0, bottom: 0) và căn chỉnh qua thuộc tính textAlign của Text.

Cái nào tốt hơn: Stack hay Overlay?

Stack và Overlay giải quyết các tác vụ khác nhau. Stack là widget bố cục để xây dựng cấu trúc màn hình với lớp phủ. Overlay là một lớp toàn cục trên toàn bộ hệ thống phân cấp widget cho các thông báo hệ thống (SnackBar, Tooltip). Theo Flutter Team (2025), hãy sử dụng Stack cho giao diện thông thường và Overlay cho thông báo tạm thời.

Tổng kết

  • Stack là widget bố cục Flutter để chồng các phần tử con dưới dạng lớp với kiểm soát qua alignment, fit và clipBehaviour.
  • Positioned đặt tọa độ chính xác của phần tử con bên trong Stack bằng top, left, right, bottom.
  • IndexedStack hiển thị một phần tử theo chỉ mục, giữ nguyên trạng thái của tất cả widget ẩn.
  • Z-index được xác định bởi thứ tự thêm phần tử con: phần tử cuối cùng được vẽ lên trên.
  • Stack được sử dụng trong 78% ứng dụng Flutter cho huy hiệu, chú giải công cụ, nội dung Cover và hoạt ảnh.
  • Sử dụng Stack khi cần chồng các phần tử, Overlay cho thông báo hệ thống.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm