Scaffold là một hàm composable trong Jetpack Compose triển khai cấu trúc màn hình cơ bản theo đặc tả Material Design. Scaffold cung cấp các slot cho TopAppBar, BottomBar, FloatingActionButton, Drawer và Snackbar. Theo Google Android Documentation, 2025, Scaffold là yếu tố bắt buộc đối với hầu hết các màn hình trong ứng dụng Compose, vì nó quản lý lề cho các thành phần hệ thống Material Design.
Các điểm chính
Scaffold là một hàm composable triển khai khung màn hình theo đặc tả Material Design. Scaffold không phải là một container theo nghĩa truyền thống — nó là một trình quản lý bố cục phối hợp vị trí của nhiều thành phần Material tiêu chuẩn trên màn hình.
Scaffold quản lý lề cho TopAppBar, BottomBar và FloatingActionButton, cũng như các yếu tố hệ thống — thanh trạng thái và thanh điều hướng. Điều này đảm bảo nội dung không chồng lấn với các yếu tố hệ thống và tuân thủ các nguyên tắc Material Design.
Theo Material Design Guidelines, 2025, Scaffold nên là phần tử gốc của mỗi màn hình ứng dụng. Nó thay thế CoordinatorLayout cũ từ hệ thống View và cung cấp một API đơn giản hơn, khai báo hơn để xây dựng bố cục màn hình.
Scaffold chấp nhận các tham số modifier, scaffoldState, topBar, bottomBar, floatingActionButton, drawerContent, snackbarHost và content. Tham số content nhận PaddingValues — các lề mà nội dung chính phải tính đến.
@Composable
fun BasicScaffold() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Trang chủ") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("Nội dung chính")
}
}
}
topBar là slot để đặt thanh trên của ứng dụng, thường là TopAppBar hoặc LargeTopAppBar. Scaffold tự động thêm lề vào innerPadding cho nội dung để không chồng lấn với topBar. TopAppBar có thể chứa tiêu đề, biểu tượng điều hướng và các hành động.
bottomBar là slot cho thanh điều hướng dưới. Nó hỗ trợ BottomNavigation, BottomAppBar hoặc NavigationBar. Scaffold tính đến chiều cao của bottomBar khi tính toán innerPadding cho nội dung. Theo Material Design Guidelines, 2025, bottomBar nên được sử dụng trên các màn hình có 3–5 điểm điều hướng.
floatingActionButton là slot cho nút hành động nổi. Scaffold tự động đặt vị trí FAB theo đặc tả Material Design — ở góc dưới bên phải với lề chính xác. floatingActionButtonPosition cho phép di chuyển FAB đến giữa cạnh dưới của màn hình.
Trong ví dụ này, Scaffold sử dụng tất cả các slot chính: TopAppBar với tiêu đề, BottomBar với ba mục điều hướng và FloatingActionButton cho hành động chính.
@Composable
fun FullScaffold() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Ứng dụng") },
navigationIcon = {
IconButton(onClick = {}) {
Icon(Icons.Default.Menu, "Menu")
}
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Default.Home, "Trang chủ") },
label = { Text("Home") },
selected = true,
onClick = {}
)
NavigationBarItem(
icon = { Icon(Icons.Default.Search, "Tìm kiếm") },
label = { Text("Search") },
selected = false,
onClick = {}
)
}
},
floatingActionButton = {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Add, "Add")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Text("Nội dung màn hình")
}
}
}
ScaffoldState là đối tượng trạng thái của Scaffold kết hợp DrawerState và SnackbarHostState. DrawerState quản lý việc mở và đóng menu bên, trong khi SnackbarHostState quản lý việc hiển thị và ẩn Snackbar. Trạng thái được tạo thông qua rememberScaffoldState() và được truyền vào tham số scaffoldState.
DrawerState cung cấp các phương thức open và close để điều khiển Drawer theo chương trình. Trạng thái của Drawer có thể là mở, đóng hoặc mở một phần. drawerGesturesEnabled cho phép tắt cử chỉ vuốt để mở Drawer, chỉ để lại việc mở theo chương trình thông qua nút.
SnackbarHostState quản lý Snackbar — các thông báo tạm thời ở cuối màn hình. Phương thức showSnackbar hiển thị Snackbar với một thông điệp và một hành động tùy chọn. Theo Material Design Guidelines, 2025, Snackbar nên được sử dụng cho các thông điệp ngắn gọn không có thông tin quan trọng.
@Composable
fun ScaffoldWithDrawer() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text("Trình đơn") },
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(Icons.Default.Menu, "Menu")
}
}
)
},
drawerContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("Mục 1")
Text("Mục 2")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Text("Nội dung")
}
}
}
Một ví dụ màn hình hoàn chỉnh với Scaffold bao gồm TopAppBar, BottomNavigation, FloatingActionButton và Snackbar để hiển thị thông báo khi nhấn FAB. ScaffoldState được truyền cho tất cả các thành phần để phối hợp trạng thái.
@Composable
fun MainScreen() {
val state = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = state,
topBar = {
TopAppBar(
title = { Text("IT Sectr") },
actions = {
IconButton(onClick = {}) {
Icon(Icons.Default.Notifications, "Notifications")
}
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(icon = { Icon(Icons.Default.Home, "Trang chủ") },
label = { Text("Home") }, selected = true, onClick = {})
NavigationBarItem(icon = { Icon(Icons.Default.Settings, "Cài đặt") },
label = { Text("Settings") }, selected = false, onClick = {})
}
},
floatingActionButton = {
FloatingActionButton(onClick = {
scope.launch {
state.snackbarHostState.showSnackbar("Hoàn thành")
}
}) {
Icon(Icons.Default.Add, "Add")
}
}
) { padding ->
Box(
modifier = Modifier.padding(padding).fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text("Chào mừng")
}
}
}
innerPadding là PaddingValues mà Scaffold truyền cho khối content. Chúng chứa các lề cần thiết để ngăn nội dung chồng lấn với statusBar, navigationBar, topBar và bottomBar. Việc sử dụng innerPadding là bắt buộc để hiển thị chính xác trên các thiết bị khác nhau.
Scaffold tự động tính toán innerPadding dựa trên lề hệ thống và chiều cao của các slot được cài đặt. Trên các thiết bị có camera dạng đảo hoặc phần cắt màn hình, Scaffold điều chỉnh lề cho thanh trạng thái. Trên các thiết bị cũ có nút vật lý, lề dưới có thể không có.
Theo Android Developers, 2024, innerPadding nên được áp dụng cho container gốc của nội dung thông qua Modifier.padding(innerPadding). Nếu nội dung sử dụng container có thể cuộn, innerPadding được áp dụng thông qua Modifier.consumeWindowInsets để bàn phím và cử chỉ hệ thống hoạt động chính xác.
Các câu hỏi thường gặp
Scaffold không phải là container mà là trình quản lý bố cục điều khiển các slot cho TopAppBar, BottomBar, FAB, Drawer và Snackbar, và tự động xử lý lề cho các thành phần hệ thống. Column và Box chỉ đặt các phần tử mà không xem xét Material Design.
Scaffold được khuyến nghị cho các màn hình sử dụng thành phần Material Design. Trên các màn hình đơn giản không có TopAppBar, BottomBar hoặc FAB, Scaffold không bắt buộc, nhưng nó cung cấp lề hệ thống chính xác thông qua innerPadding, hữu ích ngay cả khi không có slot.
Truyền ScaffoldState vào tham số scaffoldState của Scaffold, sau đó gọi state.snackbarHostState.showSnackbar(“Thông báo”) bên trong một coroutine. Scaffold sẽ tự động hiển thị Snackbar ở cuối màn hình.
Có, Scaffold tạo trạng thái nội bộ theo mặc định nếu scaffoldState không được truyền vào. Tuy nhiên, để điều khiển Drawer và Snackbar theo chương trình, bạn cần tạo và truyền ScaffoldState thông qua rememberScaffoldState().
Sử dụng tham số floatingActionButtonPosition của Scaffold. Theo mặc định, FAB ở góc dưới bên phải (FabPosition.End). Đặt FabPosition.Center để đặt FAB ở giữa cạnh dưới của màn hình.
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