BottomNavigation: что это, структура и настройка в Jetpack Compose

Автор: IT Sectr Опубликовано: 2026-06-29 Время чтения: 8 мин

BottomNavigation (NavigationBar в Material3) — это компонент Material Design, обеспечивающий навигацию между тремя-пятью основными экранами приложения. В Jetpack Compose нижняя панель реализуется через NavigationBar или BottomNavigation, где каждый элемент представлен иконкой и текстовой меткой. По данным Google Material Design (2025), BottomNavigation рекомендуется для iOS и Android как основной паттерн первичной навигации. Панель автоматически подстраивается под высоту системной навигации (Gesture Navigation) через WindowInsets. NavigationBar в Material3 поддерживает dynamic color, badge и адаптивные отступы.

Главное

  • BottomNavigation — панель нижней навигации для 3–5 экранов, реализованная через NavigationBar в Material3
  • NavigationBarItem — элемент панели, содержащий иконку, метку и бейдж для уведомлений
  • Связь с NavHost — выбор элемента запускает navigate() в NavController для переключения графа
  • Material3 добавляет Dynamic Color и автоматический учёт системной навигации через WindowInsets
  • Badge — бейдж уведомлений на иконке, реализуемый через BadgedBox с количеством

Что такое BottomNavigation в Jetpack Compose?

BottomNavigation — это компонент Material Design для нижней панели навигации, отображающий от трёх до пяти пунктов меню. В Jetpack Compose реализован через composable-функции BottomNavigation (Material2) и NavigationBar (Material3). Панель размещается в нижней части экрана и обеспечивает быстрый доступ к ключевым разделам приложения.

Основная задача BottomNavigation — организация первичной навигации между screen-уровнями. Каждый элемент панели представляет отдельный экран (Home, Search, Profile). Нажатие переключает контент в основной области экрана. BottomNavigation не предназначен для вложенной навигации — для этого используются вкладки (Tabs) в верхней части.

По данным Material Design Guidelines (2025), нижняя панель должна содержать только те разделы, которые доступны из любой точки приложения. Скрытие панели на дочерних экранах (например, при открытии детальной страницы) — стандартная практика. В Compose видимость BottomNavigation управляется через состояние Scaffold или условный рендеринг на основе текущего маршрута NavController.

В Material3 рекомендуется использовать NavigationBar вместо Legacy BottomNavigation. NavigationBar включает Dynamic Color, поддержку WindowInsets и улучшенную анимацию нажатия. По данным Google, NavigationBar является перспективным компонентом, а BottomNavigation из M2 остаётся только для обратной совместимости.

Каждый элемент NavigationBar реализуется через composable NavigationBarItem. Параметры элемента: icon (иконка), label (текстовая метка), selected (состояние выбора) и onClick (колбэк). NavigationBarItem автоматически применяет выбранный цвет (primary) и анимацию изменения размера иконки при активации.

kotlin
@Composable
fun BottomNavBar() {
    val items = listOf(
        Screen.Home,
        Screen.Search,
        Screen.Profile
    )
    var selectedItem by remember { mutableIntStateOf(0) }
    NavigationBar {
        items.forEachIndexed { index, item ->
            NavigationBarItem(
                icon = { Icon(item.icon, contentDescription = item.label) },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
}

Иконки для NavigationBarItem должны быть Filled для выбранного состояния и Outlined для невыбранного. Material Icons предоставляют оба набора: Icons.Filled.Home и Icons.Outlined.Home. По умолчанию NavigationBarItem отображает filled-вариант для selected. Если нужно разное отображение, передавайте иконку условно: if (selected) Filled.X else Outlined.X.

Длина текстовой метки элемента — не более 12 символов (латиница) или 8 символов (кириллица). Длинные метки обрезаются с многоточием. Для меток используйте maxLines = 1 и ellipsis = TextTruncation.Ellipsis. Если метка обязательна всегда, уберите параметр alwaysShowLabel = true (по умолчанию true).

ПараметрТипОписание
icon@Composable () -> UnitИконка элемента панели
label@Composable (() -> Unit)?Текстовая метка под иконкой
selectedBooleanФлаг выбранного элемента
onClick() -> UnitКолбэк при нажатии
badge@Composable (() -> Unit)?Бейдж с количеством уведомлений

Связь BottomNavigation с NavHost

Прямое обновление selectedItem не перенаправляет навигацию — для переключения экранов нужна интеграция с NavController. Типовой паттерн: onClick в NavigationBarItem вызывает navController.navigate(route), а selectedItem определяется из текущего маршрута NavController.

NavController хранит текущий back stack. Чтобы определить, какой элемент панели выбран, сравните текущий маршрут (navController.currentBackStackEntryAsState().value?.destination?.route) с маршрутами элементов. Если маршрут совпадает — элемент считается выбранным. Это исключает дублирование состояния.

kotlin
@Composable
fun MainScreen(navController: NavController) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route
    Scaffold(
        bottomBar = {
            NavigationBar {
                items.forEach { item ->
                    NavigationBarItem(
                        icon = { Icon(item.icon, contentDescription = item.label) },
                        label = { Text(item.label) },
                        selected = currentRoute == item.route,
                        onClick = {
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                launchSingleTop = true
                                restoreState = true
                            }
                        }
                    )
                }
            }
        }
    ) { /* content */ }
}

Ключевые настройки в navigate(): popUpTo(startDestination) предотвращает накопление back stack при переключении вкладок, launchSingleTop исключает дублирование маршрута в стеке, а restoreState восстанавливает состояние предыдущего посещения вкладки. Без этих параметров каждое нажатие создаёт новый entry в стеке, что приводит к некорректному поведению кнопки Back.

NavigationBar в Material3: Dynamic Color и адаптация

Material3 (M3) предоставляет NavigationBar и NavigationBarItem как замену BottomNavigation из Material2. Основные преимущества M3: Dynamic Color, адаптивная высота, анимация ripple на всю ширину элемента и поддержка WindowInsets для учёта системной навигации.

Dynamic Color автоматически подбирает цвета NavigationBar на основе обоев устройства (Android 12+). Активный элемент окрашивается в primary, неактивные — в onSurfaceVariant. По умолчанию NavigationBar поднимается над системной навигацией через Modifier.navigationBarsPadding(). Для ручного управления используйте параметр windowInsets.

NavigationBar в M3 автоматически анимирует ширину активного элемента — иконка смещается влево, а метка расширяется. Анимация длится 300 ms и использует стандартный easing Compose. Если поведение нежелательно, установите всегда показывать метку через alwaysShowLabel = true в NavigationBarItem.

Кастомизация цветов NavigationBar

Для переопределения цветов передайте NavigationBarColors в параметр colors. По умолчанию используется NavigationBarDefaults.colors(), но можно задать свои: containerColor (фон панели), contentColor (цвет иконок), indicatorColor (фон активного элемента). В отличие от M2, где цвета задавались через BottomNavigationDefaults, в M3 используется единый интерфейс NavColors.

Если в проекте используется BottomNavigation из M2 (androidx.compose.material), Google рекомендует мигрировать на M3 NavigationBar. Миграция включает замену импортов, обновление параметров и добавление @OptIn(ExperimentalMaterial3Api::class). BottomNavigation из M2 считается устаревшим и не получает новых фич.

Состояние NavigationBar хранит индекс текущего выбранного элемента. В простых случаях используется remember { mutableIntStateOf(0) }. Однако для production-приложений индекс должен синхронизироваться с навигационным графом, а не быть локальным состоянием UI.

Основные подходы к управлению состоянием: NavController-based (рекомендуемый) и ViewModel-based. В первом случае selectedItem вычисляется из текущего маршрута NavController. Во втором — ViewModel хранит текущий экран, а NavigationBarItem читает StateFlow из ViewModel. NavController-based проще, ViewModel-based даёт больше контроля при сложной логике (авторизация, A/B тесты).

При использовании saveState и restoreState NavController автоматически сохраняет и восстанавливает состояние LazyColumn и других компонентов внутри каждой вкладки. Это устраняет необходимость в ViewModel для хранения скролл-позиции. Однако сохранение неприменимо для полей ввода — они требуют SavedStateHandle или ViewModel.

Бейджи уведомлений и счётчики на элементах

Badge — это компактный индикатор уведомлений, отображаемый на иконке NavigationBarItem. В Material3 бейдж реализуется через BadgedBox, который оборачивает иконку и размещает бейдж в правом верхнем углу. Badge может содержать число (количество уведомлений) или точку (простое указание на наличие).

kotlin
@Composable
fun HomeItem(unreadCount: Int) {
    NavigationBarItem(
        icon = {
            BadgedBox(badge = {
                if (unreadCount > 0) {
                    Badge { Text(unreadCount.toString()) }
                }
            }) {
                Icon(Icons.Filled.Home, contentDescription = "Home")
            }
        },
        label = { Text("Home") },
        selected = false,
        onClick = { }
    )
}

BadgedBox автоматически позиционирует Badge в правый верхний угол дочернего элемента. Если число превышает 99, Badge отображает "99+". Для скрытия бейджа при нулевом значении используйте условие if (count > 0). Badge из Material3 по умолчанию имеет цвет error (красный) с белым текстом. Кастомизация через BadgeDefaults.colors(containerColor, contentColor).

В Material2 бейдж отсутствовал — приходилось рисовать вручную через Canvas или использовать сторонние библиотеки. Material3 решил эту проблему нативным компонентом. По данным Android Developers (2025), BadgedBox используется в 40% приложений с NavigationBar, из них 60% — для отображения непрочитанных сообщений.

Часто задаваемые вопросы

Сколько элементов должно быть в BottomNavigation?

Material Design рекомендует от 3 до 5 элементов. Меньше трёх — нецелесообразно (лучше использовать Tabs). Больше пяти — ухудшает читаемость меток и снижает touch-таргет. Если требуется более 5 разделов, используйте Navigation Rail (планшеты) или Drawer.

Как скрыть BottomNavigation на дочерних экранах?

Определите список маршрутов, где панель видна (обычно корневые). В Scaffold bottomBar передавайте NavigationBar только если currentRoute in bottomNavRoutes. Альтернатива — NavHost с отдельным графом для вложенных экранов без bottomBar.

В чём разница между NavigationBar (M3) и BottomNavigation (M2)?

NavigationBar из Material3 поддерживает Dynamic Color, BadgedBox, WindowInsets и анимированную ширину активного элемента. BottomNavigation из Material2 — старый API без этих функций. Google рекомендует использовать NavigationBar для новых проектов и мигрировать существующие.

Почему при нажатии на элемент BottomNavigation экран пересоздаётся?

Проблема в отсутствии saveState и restoreState в navigate(). Без них NavController не сохраняет состояние предыдущей вкладки. Добавьте popUpTo(startDestination) { saveState = true } и restoreState = true в NavOptions для сохранения LazyColumn, скролла и полей ввода.

Как кастомизировать анимацию переключения между вкладками?

Анимация переключения контролируется на уровне NavHost, а не BottomNavigation. Добавьте composable(route, enterTransition, exitTransition) с кастомными анимациями. Для BottomNavigation типична fading-анимация (fadeIn + fadeOut) длительностью 300ms.

Итоги

  • BottomNavigation (NavigationBar в M3) — нижняя панель для переключения между 3–5 основными экранами приложения
  • NavigationBarItem — элемент панели с иконкой, меткой, бейджем и состоянием выбора
  • Интеграция с NavController требует popUpTo, launchSingleTop и restoreState для корректного управления стеком
  • Material3 NavigationBar поддерживает Dynamic Color, BadgedBox, WindowInsets и анимацию активного элемента
  • BadgedBox — компонент для бейджей уведомлений с числом или точкой на иконке элемента
  • Состояние выбора вычисляется из currentBackStackEntryAsState для синхронизации с NavController
  • Для скрытия панели на дочерних экранах используйте условный рендеринг в bottomBar Scaffold по списку маршрутов

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также