Scaffold trong Jetpack Compose — nó là gì, thuộc tính và cách hoạt động

Tác giả: IT Sectr Đã đăng: 2026-06-29 Thời gian đọc: 11 phút

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à khung màn hình Material Design cung cấp các slot cho TopAppBar, BottomBar, FAB, Drawer và Snackbar với lề chính xác.
  • ScaffoldState quản lý trạng thái của Drawer và Snackbar, cho phép mở và đóng menu bên và hiển thị thông báo.
  • topBar, bottomBarfloatingActionButton là các slot của Scaffold để tích hợp các thành phần Material Design tiêu chuẩn.
  • drawerGesturesEnabled điều khiển cử chỉ vuốt để mở menu bên, trong khi snackbarHost điều khiển hiển thị Snackbar tùy chỉnh.
  • Scaffold tự động quản lý lề cho thanh trạng thái và thanh điều hướng thông qua innerPadding.

Scaffold trong Jetpack Compose là gì

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.

Chữ ký cơ bản của Scaffold

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.

kotlin
@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")
        }
    }
}

Các slot của Scaffold: topBar, bottomBar và floatingActionButton

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.

Ví dụ với tất cả các slot

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.

kotlin
@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: quản lý Drawer và Snackbar

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.

kotlin
@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")
        }
    }
}

Ví dụ xây dựng màn hình với Scaffold

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.

kotlin
@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 và bố cục thích ứng với Scaffold

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.

  • Áp dụng innerPadding cho container gốc của nội dung thông qua Modifier.padding(innerPadding)
  • Đối với container có thể cuộn, sử dụng Modifier.consumeWindowInsets(innerPadding)
  • Không thêm lề bổ sung cho thanh trạng thái — Scaffold xử lý điều này tự động
  • Nếu topBar hoặc bottomBar vắng mặt, các lề tương ứng trong innerPadding sẽ bằng không

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

Scaffold khác gì so với Column hoặc Box thông thường?

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.

Có bắt buộc phải sử dụng Scaffold trên mọi màn hình không?

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.

Làm thế nào để hiển thị Snackbar qua Scaffold?

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ó thể sử dụng Scaffold mà không có scaffoldState không?

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().

Làm thế nào để thay đổi vị trí FloatingActionButton trong Scaffold?

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

  • Scaffold là khung màn hình Material Design với quản lý slot cho TopAppBar, BottomBar, FAB, Drawer và Snackbar và lề hệ thống tự động.
  • topBar, bottomBarfloatingActionButton là các slot của Scaffold để tích hợp các thành phần tiêu chuẩn với định vị chính xác.
  • ScaffoldState kết hợp DrawerState và SnackbarHostState, cung cấp điều khiển theo chương trình đối với menu bên và thông báo thông qua coroutines.
  • innerPadding là tham số bắt buộc của khối content, chứa lề cho các yếu tố hệ thống và các slot được cài đặt của Scaffold.
  • drawerGesturesEnabledfloatingActionButtonPosition là các tham số bổ sung để tinh chỉnh hành vi của Scaffold.
  • Scaffold tự động xử lý lề cho thanh trạng thái và thanh điều hướng trên các thiết bị có cấu hình màn hình khác nhau.
  • Đối với các màn hình đơn giản không có thành phần Material, Scaffold không bắt buộc nhưng được khuyến nghị là phần tử gốc để xử lý lề hệ thống chính xác.

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