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 поверх поля ввода.

Sources

Related topics