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-а је организовање примарне навигације између нивоа екрана. Сваки елемент панела представља посебан екран (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 обнавља стање претходне посете картици. Без ових параметара, сваки притисак ствара нови унос у стек, што доводи до неисправног понашања дугмада Назад.

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. Миграција укључује замену import-а, ажурирање параметара и додавање @OptIn(ExperimentalMaterial3Api::class). BottomNavigation из M2 се сматра застарелим и не добија нове функције.

Стање NavigationBar-а чува индекс тренутно изабраног елемента. У једноставним случајевима користи се remember { mutableIntStateOf(0) }. Међутим, за производне апликације индекс треба да буде синхронизован са навигационим графом, а не да буде локално стање 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 = "Почетна")
            }
        },
        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-target. Ако је потребно више од 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође