Jetpack ComposeJuniorTechnical
Что такое Scaffold и какие стандартные компоненты он предоставляет?
Scaffold — Material 3 layout-компонент, предоставляющий слоты для TopAppBar, BottomBar, FAB и SnackbarHost. Он вычисляет innerPadding с учётом всех компонентов и системных вставок — его обязательно применять к контенту.
Scaffold в Jetpack Compose
Scaffold — это layout-компонент Material Design 3, который предоставляет стандартную структуру экрана: TopAppBar, BottomBar, FAB, Snackbar и основной контент. Он управляет отступами (padding), чтобы контент не перекрывался системными элементами и компонентами Material.
Сигнатура
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable (PaddingValues) -> Unit
)
Полный пример
@Composable
fun HomeScreen(
navController: NavController,
viewModel: HomeViewModel = viewModel()
) {
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
topBar = {
TopAppBar(
title = { Text("Главная") },
navigationIcon = {
IconButton(onClick = { navController.navigateUp() }) {
Icon(Icons.Default.ArrowBack, contentDescription = "Назад")
}
},
actions = {
IconButton(onClick = { /* поиск */ }) {
Icon(Icons.Default.Search, contentDescription = "Поиск")
}
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Default.Home, contentDescription = null) },
label = { Text("Главная") },
selected = true,
onClick = {}
)
NavigationBarItem(
icon = { Icon(Icons.Default.Person, contentDescription = null) },
label = { Text("Профиль") },
selected = false,
onClick = { navController.navigate("profile") }
)
}
},
floatingActionButton = {
FloatingActionButton(onClick = {
scope.launch {
snackbarHostState.showSnackbar("Новый элемент добавлен")
}
}) {
Icon(Icons.Default.Add, contentDescription = "Добавить")
}
},
snackbarHost = { SnackbarHost(snackbarHostState) }
) { innerPadding -> // innerPadding — обязательно применять!
LazyColumn(
modifier = Modifier.padding(innerPadding) // критично
) {
items(20) { index ->
Text("Элемент $index", Modifier.padding(16.dp))
}
}
}
}
Стандартные компоненты Scaffold
- TopAppBar / MediumTopAppBar / LargeTopAppBar — верхняя панель с заголовком, навигацией и действиями
- BottomAppBar / NavigationBar — нижняя навигация
- FloatingActionButton / ExtendedFloatingActionButton — плавающая кнопка действия
- SnackbarHost — область для отображения уведомлений
- Drawer (DrawerLayout) — боковое меню, передаётся отдельно через
ModalNavigationDrawer
PaddingValues и WindowInsets
Лямбда content получает PaddingValues, содержащий отступы от TopBar, BottomBar и системных вставок (status bar, navigation bar). Это значение обязательно применять к корневому контенту через Modifier.padding(innerPadding).
Подводные камни
- Игнорирование
innerPadding— контент уходит под TopAppBar или BottomBar, особенно на устройствах с жестовой навигацией. - Применение
innerPaddingдважды (к Scaffold и к дочернему элементу) — двойные отступы. - Вложенные Scaffold — правила padding-а конфликтуют, системные вставки считаются дважды.
- Использование устаревшего
Scaffoldиз Material 2 вместе с компонентами Material 3 — несовместимые темы и цвета. - Создание
SnackbarHostStateвнутриsnackbarHost-лямбды безremember— новый state при каждой рекомпозиции, снэкбары теряются. - FAB перекрывает последний элемент
LazyColumn— добавляйте нижний отступ, равный высоте FAB, либо используйтеinnerPadding. - Установка
contentWindowInsets = WindowInsets(0)без понимания последствий — контент уходит под системные бары.
Common mistakes
- Объяснять «Scaffold» только как синтаксис и не описывать поведение runtime/compiler.
- Игнорировать важный риск: Игнорирование padding от Scaffold часто дает обрезанный список или FAB поверх поля ввода.
- Давать пример без edge case: отмены, null, recomposition, platform boundary или ошибки.
What the interviewer is testing
- Формулирует суть темы «Scaffold» своими словами и связывает ее с кодом.
- Называет механизм: Контент должен учитывать innerPadding, иначе UI перекрывается системными или scaffold elements.
- Видит production-последствие: Игнорирование padding от Scaffold часто дает обрезанный список или FAB поверх поля ввода.