BottomNavigation: co to je, struktura a nastavení v Jetpack Compose

Autor: IT Sectr Publikováno: 2026-06-29 Doba čtení: 8 min

BottomNavigation (NavigationBar v Material3) je komponenta Material Design, která zajišťuje navigaci mezi třemi až pěti hlavními obrazovkami aplikace. V Jetpack Compose je spodní panel implementován pomocí NavigationBar nebo BottomNavigation, kde každý prvek je reprezentován ikonou a textovým štítkem. Podle Google Material Design (2025) je BottomNavigation doporučen pro iOS a Android jako hlavní vzor primární navigace. Panel se automaticky přizpůsobuje výšce systémové navigace (Gesture Navigation) pomocí WindowInsets. NavigationBar v Material3 podporuje dynamic color, badge a adaptivní okraje.

Hlavní body

  • BottomNavigation — panel spodní navigace pro 3–5 obrazovek, implementovaný pomocí NavigationBar v Material3
  • NavigationBarItem — prvek panelu obsahující ikonu, štítek a odznak pro oznámení
  • Propojení s NavHost — výběr prvku spouští navigate() v NavController pro přepnutí grafu
  • Material3 přidává Dynamic Color a automatické zohlednění systémové navigace pomocí WindowInsets
  • Badge — odznak oznámení na ikoně, implementovaný pomocí BadgedBox s číslem

Co je BottomNavigation v Jetpack Compose?

BottomNavigation je komponenta Material Design pro spodní navigační panel, který zobrazuje tři až pět položek nabídky. V Jetpack Compose je implementován pomocí composable funkcí BottomNavigation (Material2) a NavigationBar (Material3). Panel je umístěn ve spodní časti obrazovky a poskytuje rychlý přístup k klíčovým sekcím aplikace.

Hlavním úkolem BottomNavigation je organizace primární navigace mezi úrovněmi obrazovek. Každý prvek panelu představuje samostatnou obrazovku (Home, Search, Profile). Stisknutí přepíná obsah v hlavní oblasti obrazovky. BottomNavigation není určen pro vnořenou navigaci — k tomu slouží karty (Tabs) v horní časti.

Podle Material Design Guidelines (2025) by měl spodní panel obsahovat pouze ty sekce, které jsou přístupné z jakéhokoliv místa aplikace. Skrytí panelu na dceřiných obrazovkách (např. při otevření stránky s detaily) je běžnou praxí. V Compose je viditelnost BottomNavigation řízena pomocí stavu Scaffold nebo podmínečného vykreslení na základě aktuální trasy NavController.

V Material3 se doporučuje používat NavigationBar místo Legacy BottomNavigation. NavigationBar obsahuje Dynamic Color, podporu WindowInsets a vylepšenou animaci stisknutí. Podle Google je NavigationBar perspektivní komponentou, zatímco BottomNavigation z M2 zůstává pouze pro zpětnou kompatibilitu.

Každý prvek NavigationBar je implementován pomocí composable NavigationBarItem. Parametry prvku: icon (ikona), label (textový štítek), selected (stav výběru) a onClick (zpětné volání). NavigationBarItem automaticky aplikuje vybranou barvu (primary) a animaci změny velikosti ikony při aktivaci.

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 }
            )
        }
    }
}

Ikony pro NavigationBarItem by měly být Filled pro vybraný stav a Outlined pro nevybraný stav. Material Icons poskytují obě sady: Icons.Filled.Home a Icons.Outlined.Home. Ve výchozím nastavení NavigationBarItem zobrazuje filled variantu pro selected. Pokud je potřeba jiné zobrazení, předejte ikonu podmínečně: if (selected) Filled.X else Outlined.X.

Délka textového štítku prvku — ne více než 12 znaků (latinka) nebo 8 znaků (cyrilice). Dlouhé štítky jsou zkráceny troma tečkami. Pro štítky používejte maxLines = 1 a ellipsis = TextTruncation.Ellipsis. Pokud je štítek vždy potřeba, nastavte parametr alwaysShowLabel = true (výchozí true).

ParametrTypPopis
icon@Composable () -> UnitIkona prvku panelu
label@Composable (() -> Unit)?Textový štítek pod ikonou
selectedBooleanPříznak vybraného prvku
onClick() -> UnitZpětné volání při stisknutí
badge@Composable (() -> Unit)?Odznak s počtem oznámení

Propojení BottomNavigation s NavHost

Přímá aktualizace selectedItem nepřesměrovává navigaci — pro přepínání obrazovek je potřebná integrace s NavController. Typický vzor: onClick v NavigationBarItem volá navController.navigate(route) a selectedItem je určen z aktuální trasy NavController.

NavController ukládá aktuální back stack. Pro určení, který prvek panelu je vybrán, porovnejte aktuální trasu (navController.currentBackStackEntryAsState().value?.destination?.route) s trasami prvků. Pokud se trasa shoduje — prvek je považován za vybraný. Toto eliminuje duplikací stavu.

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 */ }
}

Klíčové nastavení v navigate(): popUpTo(startDestination) zabraňuje hromadění back stack při přepínání karet, launchSingleTop vylučuje duplikací trasy v zásobníku a restoreState obnovuje stav předchozí návštěvy karty. Bez těchto parametrů každý stisk vytváří nový záznam v zásobníku, co vede k nesprávnému chování tlačítka Zpět.

NavigationBar v Material3: Dynamic Color a adaptace

Material3 (M3) poskytuje NavigationBar a NavigationBarItem jako náhradu za BottomNavigation z Material2. Hlavní výhody M3: Dynamic Color, adaptivní výška, ripple animace přes celou šířku prvku a podpora WindowInsets pro zohlednění systémové navigace.

Dynamic Color automaticky vybírá barvy NavigationBar na základě tapety zařízení (Android 12+). Aktivní prvek je obarven primary, neaktivní — onSurfaceVariant. Ve výchozím nastavení se NavigationBar zvedá nad systémovou navigací pomocí Modifier.navigationBarsPadding(). Pro ruční řízení použijte parametr windowInsets.

NavigationBar v M3 automaticky animuje šířku aktivního prvku — ikona se posouví doleva a štítek se rozširuje. Animace trvá 300 ms a používí standardní easing Compose. Pokud je toto chování nežádoucí, nastavte štítek vždy viditelný pomocí alwaysShowLabel = true v NavigationBarItem.

Přizpůsobení barev NavigationBar

Pro přepsání barev předejte NavigationBarColors v parametru colors. Ve výchozím nastavení se používá NavigationBarDefaults.colors(), ale můžete nastavit vlastní barvy: containerColor (pozadí panelu), contentColor (barva ikon), indicatorColor (pozadí aktivního prvku). Na rozdíl od M2, kde barvy byly nastavovány pomocí BottomNavigationDefaults, v M3 se používá jednotné rozhraní NavColors.

Pokud se v projektu používá BottomNavigation z M2 (androidx.compose.material), Google doporučuje migraci na M3 NavigationBar. Migrace zahrnuje výměnu importů, aktualizaci parametrů a přidání @OptIn(ExperimentalMaterial3Api::class). BottomNavigation z M2 je považován za zastaralý a nedostává nové funkce.

Stav NavigationBar ukládá index aktuálně vybraného prvku. V jednoduchých případech se používá remember { mutableIntStateOf(0) }. Pro produkční aplikace by však měl být index synchronizován s navigačním grafem, nikoli být lokálním stavem UI.

Hlavní přístupy k řízení stavu: NavController-based (doporučený) a ViewModel-based. V prvním případě je selectedItem vypočítán z aktuální trasy NavController. Ve druhém — ViewModel ukládá aktuální obrazovku a NavigationBarItem čte StateFlow z ViewModel. NavController-based je jednodušší, ViewModel-based poskytuje více kontroly při složité logice (autorizace, A/B testy).

Při použití saveState a restoreState NavController automaticky ukládá a obnovuje stav LazyColumn a dalších komponent uvnitř každé karty. Tím odpadá potřeba ViewModel pro ukládání pozice posouvání. Ukládání se však nevztahuje na vstupní pole — ta vyžadují SavedStateHandle nebo ViewModel.

Odznaky oznámení a počítadla na prvcích

Badge je kompaktní ukazatel oznámení zobrazovaný na ikoně NavigationBarItem. V Material3 je odznak implementován pomocí BadgedBox, který obaluje ikonu a umístí odznak do pravého horního rohu. Badge může obsahovat číslo (množství oznámení) nebo tečku (jednoduchý ukazatel přítomnosti).

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

BadgedBox automaticky pozicuje Badge do pravého horního rohu dceřiného prvku. Pokud číslo přesahuje 99, Badge zobrazuje „99+“. Pro skrytí odznaku při nulové hodnotě použijte podmínku if (count > 0). Badge z Material3 má ve výchozím nastavení barvu error (červenou) s bílým textem. Přizpůsobení pomocí BadgeDefaults.colors(containerColor, contentColor).

V Material2 odznak neexistoval — musel být ručně kreslen pomocí Canvas nebo použít knihovny třetích stran. Material3 vyřešil tento problém nativní komponentou. Podle Android Developers (2025) je BadgedBox používán ve 40% aplikací s NavigationBar, z čehož 60% pro zobrazení nepřečtených zpráv.

Často kladené otázky

Kolik prvků by mělo být v BottomNavigation?

Material Design doporučuje 3 až 5 prvků. Méně než tři — neúčelné (lepší použít Tabs). Více než pět — zhoršuje čitelnost štítků a snižuje touch-target. Pokud je potřeba více než 5 sekcí, použijte Navigation Rail (tablety) nebo Drawer.

Jak skrýt BottomNavigation na dceřiných obrazovkách?

Určete seznam tras, kde je panel viditelný (obvykle kořenové trasy). V Scaffold bottomBar předejte NavigationBar pouze pokud je currentRoute v bottomNavRoutes. Alternativa — NavHost s odděleným grafem pro vnořené obrazovky bez bottomBar.

Jaký je rozdíl mezi NavigationBar (M3) a BottomNavigation (M2)?

NavigationBar z Material3 podporuje Dynamic Color, BadgedBox, WindowInsets a animovanou šířku aktivního prvku. BottomNavigation z Material2 je staré API bez těchto funkcí. Google doporučuje používat NavigationBar pro nové projekty a migraci stávajících.

Proč se při stisknutí prvku BottomNavigation obrazovka znovu vytváří?

Problém je v chybějících saveState a restoreState v navigate(). Bez nich NavController neukládá stav předchozí karty. Přidejte popUpTo(startDestination) { saveState = true } a restoreState = true do NavOptions pro zachování LazyColumn, posouvání a vstupních polí.

Jak přizpůsobit animaci přepínání mezi kartami?

Animace přepínání je řízena na úrovni NavHost, nikoli BottomNavigation. Přidejte composable(route, enterTransition, exitTransition) s vlastními animacemi. Pro BottomNavigation je typická fading animace (fadeIn + fadeOut) s dobou trvání 300ms.

Shrnutí

  • BottomNavigation (NavigationBar v M3) — spodní panel pro přepínání mezi 3–5 hlavními obrazovkami aplikace
  • NavigationBarItem — prvek panelu s ikonou, štítkem, odznakem a stavem výběru
  • Integrace s NavController vyžaduje popUpTo, launchSingleTop a restoreState pro správnou správu zásobníku
  • Material3 NavigationBar podporuje Dynamic Color, BadgedBox, WindowInsets a animaci aktivního prvku
  • BadgedBox — komponenta pro odznaky oznámení s číslem nebo tečkou na ikoně prvku
  • Stav výběru je vypočítán z currentBackStackEntryAsState pro synchronizaci s NavController
  • Pro skrytí panelu na dceřiných obrazovkách použijte podmínečné vykreslení v Scaffold bottomBar na základě seznamu tras

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také