Scaffold là một widget khung trung tâm trong Flutter triển khai cấu trúc màn hình Material Design cơ bản với AppBar, Drawer, BottomNavigationBar và FloatingActionButton. Nó không chỉ định vị các phần tử mà còn cung cấp một kiến trúc có sẵn với thứ tự z chính xác và xử lý cử chỉ người dùng. Theo Tài liệu Flutter (2026), Scaffold là một phần tử gốc bắt buộc để các thành phần Material hoạt động chính xác, bao gồm SnackBar và BottomSheet. Không giống như Container hay Column, Scaffold tự động quản lý SafeArea, nền thích ứng và hành vi bàn phím, khiến nó trở thành tiêu chuẩn để xây dựng màn hình trong các ứng dụng di động.
Những điểm chính
Scaffold là một widget bố cục từ thư viện Material Design tạo thành cấu trúc trực quan cơ bản của màn hình. Nó triển khai mẫu ứng dụng Material tiêu chuẩn: thanh trên cùng, thân màn hình, menu bên và các nút nổi. Không giống như Container chỉ đặt kích thước và kiểu, Scaffold quản lý toàn bộ màn hình như một hệ thống thống nhất.
Mọi ứng dụng Flutter được xây dựng với Material Design đều bắt đầu bằng MaterialApp, bên trong đó mỗi màn hình được bọc trong một Scaffold. Chính Scaffold đảm bảo hiển thị chính xác AppBar trên tất cả các thiết bị, có tính đến SafeArea cho iPhone X trở lên. Nó cũng chịu trách nhiệm về thứ tự z: Drawer hiển thị phía trên body và SnackBar hiển thị phía trên toàn bộ giao diện nhưng bên dưới các cửa sổ hội thoại.
Theo Tài liệu tham khảo API Flutter (2026), Scaffold là yêu cầu bắt buộc để các widget như SnackBar, BottomSheet và PersistentBottomSheet hoạt động chính xác. Nếu không có Scaffold, các thành phần này sẽ ném ra ngoại lệ vì chúng phụ thuộc vào InheritedWidget Scaffold.of để truy cập ngữ cảnh Scaffold. Do đó, đặt bất kỳ nội dung nào trực tiếp vào Container thay vì Scaffold là lỗi phổ biến của nhà phát triển mới bắt đầu, dẫn đến mất chức năng Material.
Trong một ứng dụng Flutter điển hình, Scaffold nằm bên trong MaterialApp, thường dưới WidgetsApp hoặc CupertinoApp trong phát triển đa nền tảng. MaterialApp tạo Navigator và chủ đề, trong khi Scaffold xác định cấu trúc của màn hình hiện tại. Mỗi màn hình có thể có Scaffold riêng với AppBar, Drawer và BottomNavigationBar độc đáo, cho phép tùy chỉnh giao diện linh hoạt cho một trang cụ thể.
Để tùy chỉnh nâng cao, Scaffold có thể được kết hợp với NestedScrollView — điều này cho phép triển khai các hiệu ứng cuộn phức tạp, như AppBar thu gọn với hiệu ứng parallax. Tuy nhiên, trong hầu hết các trường hợp, Scaffold tiêu chuẩn với các thuộc tính của nó là đủ để xây dựng một màn hình sẵn sàng sản xuất.
Scaffold bao gồm một số vùng bắt buộc và tùy chọn, mỗi vùng chịu trách nhiệm cho một phần cụ thể của giao diện. Hãy xem xét từng phần riêng lẻ để hiểu cách chúng tương tác với nhau.
AppBar là thanh trên cùng của màn hình hiển thị tiêu đề, nút điều hướng và các hành động. Nó tự động có chiều cao chính xác 56dp và tính đến SafeArea cho các thiết bị có notch. TabBar có thể được nhúng trong AppBar để chuyển đổi tab bằng thuộc tính bottom.
Drawer là một bảng trượt ra từ bên trái và cung cấp điều hướng qua các phần của ứng dụng. Scaffold tự động thêm biểu tượng hamburger vào AppBar nếu thuộc tính drawer được đặt. Drawer có thể chứa DrawerHeader, ListView với các mục menu và thông tin người dùng.
BottomNavigationBar là bảng ở cuối màn hình để chuyển đổi giữa các phần chính. Nó chứa 2 đến 5 biểu tượng có nhãn và hỗ trợ Material 3 với chỉ báo hoạt động qua NavigationBar. Scaffold đặt nó ở cuối màn hình, tự động đẩy body lên trên để nội dung không bị che khuất.
FloatingActionButton là nút tròn được đặt ở góc dưới bên phải của màn hình. Nó được sử dụng cho hành động chính trên màn hình, như thêm mục mới hoặc gửi tin nhắn. Scaffold hỗ trợ nhiều FAB với hoạt ảnh mở rộng thông qua các thuộc tính extended.
Body là vùng trung tâm của màn hình nơi nội dung chính được đặt. Bất kỳ widget nào cũng có thể làm body: Column, ListView, GridView, Stack hoặc tổ hợp tùy chỉnh. Scaffold tự động tính toán chiều cao của body có tính đến AppBar, BottomNavigationBar và SafeArea, loại bỏ nhu cầu điều chỉnh kích thước thủ công.
Scaffold cung cấp hơn 30 thuộc tính để tùy chỉnh giao diện và hành vi của màn hình. Trong số đó có cả thuộc tính bắt buộc (body) và tùy chọn kiểm soát lề, nền và trạng thái bàn phím. Hiểu từng thuộc tính giúp cấu hình chính xác giao diện theo yêu cầu thiết kế.
| Thuộc tính | Loại | Mô tả |
|---|---|---|
| appBar | PreferredSizeWidget? | Thanh trên cùng của màn hình với tiêu đề và hành động |
| body | Widget? | Nội dung chính của màn hình giữa AppBar và BottomNavigationBar |
| drawer | Widget? | Bảng bên mở bằng cách vuốt từ trái |
| bottomNavigationBar | Widget? | Thanh điều hướng dưới cùng (BottomNavigationBar hoặc NavigationBar) |
| floatingActionButton | Widget? | Nút hành động nổi |
| backgroundColor | Color? | Màu nền của Scaffold, ghi đè chủ đề |
| resizeToAvoidBottomInset | bool | Tự động thu nhỏ body khi bàn phím xuất hiện |
Thuộc tính resizeToAvoidBottomInset đặc biệt quan trọng khi làm việc với biểu mẫu: khi được đặt thành true (mặc định), Scaffold tự động thu nhỏ body khi bàn phím xuất hiện, giữ cho trường nhập liệu hiển thị. Điều này loại bỏ nhu cầu viết trình lắng nghe bàn phím tùy chỉnh. Đối với các tình huống tùy chỉnh với SingleChildScrollView, thuộc tính có thể bị tắt.
Hãy xem xét một ví dụ thực tế về Scaffold với đầy đủ các thành phần Material. Mã tạo một màn hình với AppBar, Drawer, BottomNavigationBar, FloatingActionButton và SnackBar — cấu trúc ứng dụng di động điển hình trong Flutter.
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int selectedIndex = 0;
final List<Widget> pages = [
const HomePage(),
const SearchPage(),
const ProfilePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Ứng dụng của tôi'),
actions: [
IconButton(icon: const Icon(Icons.search), onPressed: () {}),
],
),
body: pages[selectedIndex],
drawer: Drawer(
child: ListView(
children: [
const DrawerHeader(child: Text('Menu')),
ListTile(title: const Text('Cài đặt'), onTap: () {}),
],
),
),
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (int index) {
setState(() => selectedIndex = index);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(icon: Icon(Icons.search), label: 'Search'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Hành động hoàn tất')),
);
},
child: const Icon(Icons.add),
),
);
}
}
Ví dụ sử dụng NavigationBar từ Material 3 thay vì BottomNavigationBar cũ. Scaffold tự động liên kết tất cả các phần: khi Drawer mở, SnackBar ẩn đi; khi chuyển tab, body được cập nhật; và FloatingActionButton vẫn giữ nguyên vị trí. ScaffoldMessenger đảm bảo hiển thị SnackBar ngay cả khi có nhiều Scaffold trong hệ thống phân cấp.
Material 3 (M3) là sự phát triển của Material Design được Google giới thiệu vào năm 2023. Scaffold trong M3 đã nhận được một số thay đổi: widget NavigationBar mới thay vì BottomNavigationBar, hỗ trợ Màu động qua ColorScheme.fromSeed và các khuyến nghị cập nhật về lề và chiều cao thành phần. Chuyển sang M3 không yêu cầu thay thế Scaffold — chỉ cần cập nhật chủ đề ứng dụng.
Để bật Material 3 trong một dự án, cần đặt useMaterial3: true trong ThemeData. Scaffold tự động điều chỉnh kiểu trực quan: lề trở nên lớn hơn (24dp thay vì 16dp), màu sắc được tính toán lại từ màu gốc và FloatingActionButton có kích thước lớn hơn với các góc bo tròn. Đồng thời, tất cả các thuộc tính Scaffold hiện có vẫn hoạt động.
Theo Đặc tả Material Design 3 (2026), khuyến nghị sử dụng NavigationBar với ba đến năm mục thay vì BottomNavigationBar. Scaffold hỗ trợ cả hai tùy chọn, nhưng NavigationBar cung cấp hỗ trợ tốt hơn cho khả năng thích ứng và Material You trên các thiết bị Pixel.
Để Scaffold hoạt động chính xác với M3, chỉ cần cập nhật chủ đề và thay thế BottomNavigationBar bằng NavigationBar. Màu sắc sẽ tự động được tính toán qua ColorScheme.fromSeed và Scaffold sẽ điều chỉnh lề theo các tiêu chuẩn mới. Nếu cần, có thể ghi đè backgroundColor trực tiếp.
Câu hỏi thường gặp
Scaffold là một widget khung cho toàn bộ màn hình với hỗ trợ các thành phần Material (AppBar, Drawer, SnackBar), trong khi Container là một vùng chứa cơ bản cho một phần tử con duy nhất mà không quản lý cấu trúc màn hình. Scaffold cung cấp thứ tự z, SafeArea và xử lý cử chỉ tự động, còn Container chỉ đặt kích thước và kiểu.
Về mặt kỹ thuật là có, nhưng trên thực tế không được khuyến nghị. Nếu bạn lồng một Scaffold bên trong một Scaffold khác, Scaffold bên trong sẽ tạo AppBar và BottomNavigationBar riêng, dẫn đến trùng lặp bảng và nhầm lẫn thứ tự z. Đối với bố cục phức tạp, hãy sử dụng NestedScrollView hoặc Column bên trong body của một Scaffold duy nhất.
SnackBar là thành phần con của Scaffold và yêu cầu sự hiện diện của nó trong cây widget để hiển thị chính xác. Scaffold cung cấp ScaffoldState, qua đó SnackBar tạo hoạt ảnh xuất hiện và được đặt ở cuối màn hình. Nếu không có Scaffold, SnackBar sẽ ném ra ngoại lệ vì không thể tìm thấy ScaffoldState trong ngữ cảnh qua InheritedWidget.
Màu nền của Scaffold được đặt qua thuộc tính backgroundColor, ghi đè màu chủ đề cho một màn hình cụ thể. Nếu cần thay đổi nền trên toàn cục, hãy đặt scaffoldBackgroundColor trong ThemeData. Material 3 tự động tính toán màu nền từ ColorScheme.surface, nhưng có thể ghi đè thủ công.
ScaffoldMessenger là một tiện ích được giới thiệu trong Flutter 2.5 cho phép hiển thị SnackBar và BottomSheet độc lập với Scaffold hiện tại trong hệ thống phân cấp. Không giống như Scaffold.of(context) cũ, ScaffoldMessenger hoạt động chính xác khi điều hướng giữa các màn hình và không mất ngữ cảnh trong khi hoạt ảnh. Khuyến nghị luôn sử dụng ScaffoldMessenger để phản hồi người dùng.
Tổng kết
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.
Đọc thêm