BottomNavigation : structure et configuration dans Jetpack Compose

Auteur : IT Sectr Publié le : 2026-06-29 Temps de lecture : 8 min

BottomNavigation (NavigationBar dans Material3) est un composant Material Design qui assure la navigation entre trois à cinq écrans principaux d'une application. Dans Jetpack Compose, la barre inférieure est implémentée via NavigationBar ou BottomNavigation, où chaque élément est représenté par une icône et un libellé textuel. Selon Google Material Design (2025), BottomNavigation est recommandé pour iOS et Android comme motif principal de navigation primaire. Le panneau s'adapte automatiquement à la hauteur de la navigation système (Gesture Navigation) via WindowInsets. NavigationBar dans Material3 prend en charge la couleur dynamique, le badge et les marges adaptatives.

Points clés

  • BottomNavigation — barre de navigation inférieure pour 3 à 5 écrans, implémentée via NavigationBar dans Material3
  • NavigationBarItem — élément du panneau contenant une icône, un libellé et un badge pour les notifications
  • Connexion avec NavHost — la sélection d'un élément déclenche navigate() dans NavController pour changer le graphe
  • Material3 ajoute la Couleur Dynamique et la gestion automatique de la navigation système via WindowInsets
  • Badge — badge de notifications sur l'icône, implémenté via BadgedBox avec un compteur

Qu'est-ce que BottomNavigation dans Jetpack Compose ?

BottomNavigation est un composant Material Design pour une barre de navigation inférieure affichant trois à cinq éléments de menu. Dans Jetpack Compose, il est implémenté via les fonctions composables BottomNavigation (Material2) et NavigationBar (Material3). La barre est placée en bas de l'écran et fournit un accès rapide aux sections clés de l'application.

L'objectif principal de BottomNavigation est d'organiser la navigation primaire entre les destinations au niveau de l'écran. Chaque élément du panneau représente un écran distinct (Accueil, Recherche, Profil). Le tapotement change le contenu dans la zone principale de l'écran. BottomNavigation n'est pas destiné à la navigation imbriquée — des onglets en haut sont utilisés à cette fin.

Selon les Directives de Conception Material Design (2025), la barre inférieure ne doit contenir que des sections accessibles depuis n'importe quel point de l'application. Masquer la barre sur les écrans enfants (par exemple, lors de l'ouverture d'une page de détail) est une pratique standard. Dans Compose, la visibilité de BottomNavigation est contrôlée via l'état de Scaffold ou le rendu conditionnel basé sur la route actuelle de NavController.

Dans Material3, il est recommandé d'utiliser NavigationBar plutôt que l'ancienne BottomNavigation. NavigationBar inclut la Couleur Dynamique, la prise en charge de WindowInsets et une animation de pression améliorée. Selon Google, NavigationBar est le composant d'avenir, tandis que BottomNavigation de M2 reste uniquement pour la rétrocompatibilité.

Chaque élément de NavigationBar est implémenté via le composable NavigationBarItem. Paramètres : icon (icône), label (libellé textuel), selected (état de sélection) et onClick (rappel). NavigationBarItem applique automatiquement la couleur sélectionnée (primary) et anime le changement de taille de l'icône lors de l'activation.

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

Les icônes pour NavigationBarItem doivent être remplies (Filled) pour l'état sélectionné et contournées (Outlined) pour l'état non sélectionné. Material Icons fournit les deux ensembles : Icons.Filled.Home et Icons.Outlined.Home. Par défaut, NavigationBarItem affiche la variante remplie pour sélectionné. Si un affichage différent est nécessaire, transmettez l'icône conditionnellement : if (selected) Filled.X else Outlined.X.

La longueur du libellé textuel ne doit pas dépasser 12 caractères (latin) ou 8 caractères (cyrillique). Les libellés longs sont tronqués avec des points de suspension. Pour les libellés, utilisez maxLines = 1 et ellipsis = TextTruncation.Ellipsis. Si le libellé doit toujours être affiché, supprimez le paramètre alwaysShowLabel = true (la valeur par défaut est true).

ParamètreTypeDescription
icon@Composable () -> UnitIcône de l'élément du panneau
label@Composable (() -> Unit)?Libellé textuel sous l'icône
selectedBooleanIndicateur d'élément sélectionné
onClick() -> UnitRappel au clic
badge@Composable (() -> Unit)?Badge avec compteur de notifications

Connecter BottomNavigation avec NavHost

Mettre à jour directement selectedItem ne déclenche pas la navigation — pour changer d'écran, une intégration avec NavController est nécessaire. Le motif typique : onClick dans NavigationBarItem appelle navController.navigate(route), et selectedItem est déterminé à partir de la route actuelle de NavController.

NavController stocke la pile de retour actuelle. Pour déterminer quel élément du panneau est sélectionné, comparez la route actuelle (navController.currentBackStackEntryAsState().value?.destination?.route) avec les routes des éléments. Si la route correspond, l'élément est considéré comme sélectionné. Cela élimine la duplication d'état.

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

Paramètres clés dans navigate() : popUpTo(startDestination) empêche l'accumulation de la pile de retour lors du changement d'onglets, launchSingleTop empêche la duplication de routes dans la pile, et restoreState restaure l'état de l'onglet visité précédemment. Sans ces paramètres, chaque clic crée une nouvelle entrée dans la pile, ce qui entraîne un comportement incorrect du bouton Retour.

NavigationBar dans Material3 : Couleur Dynamique et adaptation

Material3 (M3) fournit NavigationBar et NavigationBarItem en remplacement de BottomNavigation de Material2. Principaux avantages de M3 : Couleur Dynamique, hauteur adaptative, animation ripple pleine largeur et prise en charge de WindowInsets pour la navigation système.

La Couleur Dynamique sélectionne automatiquement les couleurs de NavigationBar en fonction du fond d'écran de l'appareil (Android 12+). L'élément actif est coloré en primary, les inactifs en onSurfaceVariant. Par défaut, NavigationBar s'élève au-dessus de la navigation système via Modifier.navigationBarsPadding(). Pour un contrôle manuel, utilisez le paramètre windowInsets.

NavigationBar dans M3 anime automatiquement la largeur de l'élément actif — l'icône se déplace vers la gauche et le libellé se développe. L'animation dure 300 ms et utilise l'easing standard de Compose. Si ce comportement n'est pas souhaité, affichez toujours le libellé via alwaysShowLabel = true dans NavigationBarItem.

Personnalisation des couleurs de NavigationBar

Pour remplacer les couleurs, transmettez NavigationBarColors dans le paramètre colors. Par défaut, NavigationBarDefaults.colors() est utilisé, mais des valeurs personnalisées peuvent être définies : containerColor (fond de la barre), contentColor (couleur des icônes), indicatorColor (fond de l'élément actif). Contrairement à M2 où les couleurs étaient définies via BottomNavigationDefaults, M3 utilise une interface unifiée NavColors.

Si le projet utilise BottomNavigation de M2 (androidx.compose.material), Google recommande de migrer vers NavigationBar de M3. La migration inclut le remplacement des imports, la mise à jour des paramètres et l'ajout de @OptIn(ExperimentalMaterial3Api::class). BottomNavigation de M2 est considéré comme obsolète et ne reçoit pas de nouvelles fonctionnalités.

L'état de NavigationBar stocke l'index de l'élément actuellement sélectionné. Dans les cas simples, on utilise remember { mutableIntStateOf(0) }. Cependant, pour les applications de production, l'index doit être synchronisé avec le graphe de navigation, et non être un état local de l'interface utilisateur.

Les principales approches de gestion d'état : basée sur NavController (recommandée) et basée sur ViewModel. Dans le premier cas, selectedItem est calculé à partir de la route actuelle de NavController. Dans le second, le ViewModel stocke l'écran actuel, et NavigationBarItem lit le StateFlow du ViewModel. L'approche basée sur NavController est plus simple ; celle basée sur ViewModel offre plus de contrôle pour une logique complexe (authentification, tests A/B).

Lors de l'utilisation de saveState et restoreState, NavController sauvegarde et restaure automatiquement l'état de LazyColumn et d'autres composants dans chaque onglet. Cela élimine le besoin d'un ViewModel pour stocker la position de défilement. Cependant, la sauvegarde ne s'applique pas aux champs de saisie — ils nécessitent SavedStateHandle ou ViewModel.

Badges de notification et compteurs sur les éléments

Badge est un indicateur compact de notifications affiché sur l'icône de NavigationBarItem. Dans Material3, le badge est implémenté via BadgedBox, qui enveloppe l'icône et place le badge dans le coin supérieur droit. Le badge peut contenir un nombre (compteur de notifications) ou un point (indicateur simple de présence).

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

BadgedBox positionne automatiquement le Badge dans le coin supérieur droit de l'élément enfant. Si le compteur dépasse 99, le Badge affiche « 99+ ». Pour masquer le badge lorsque la valeur est nulle, utilisez if (count > 0). Le Badge de Material3 a par défaut la couleur error (rouge) avec du texte blanc. Personnalisez via BadgeDefaults.colors(containerColor, contentColor).

Dans Material2, le badge était absent — il devait être dessiné manuellement via Canvas ou en utilisant des bibliothèques tierces. Material3 a résolu ce problème avec un composant natif. Selon Android Developers (2025), BadgedBox est utilisé dans 40 % des applications avec NavigationBar, dont 60 % affichent des messages non lus.

Foire aux questions

Combien d'éléments doivent figurer dans BottomNavigation ?

Material Design recommande 3 à 5 éléments. Moins de trois n'est pas pratique (utilisez plutôt des onglets). Plus de cinq réduit la lisibilité des libellés et la taille de la cible tactile. Si plus de 5 sections sont nécessaires, utilisez Navigation Rail (tablettes) ou Drawer.

Comment masquer BottomNavigation sur les écrans enfants ?

Définissez une liste de routes où la barre est visible (généralement les routes racines). Dans le bottomBar de Scaffold, transmettez NavigationBar uniquement si currentRoute est dans bottomNavRoutes. Une alternative est NavHost avec un graphe séparé pour les écrans imbriqués sans bottomBar.

Quelle est la différence entre NavigationBar (M3) et BottomNavigation (M2) ?

NavigationBar de Material3 prend en charge la Couleur Dynamique, BadgedBox, WindowInsets et la largeur animée de l'élément actif. BottomNavigation de Material2 est une API legacy sans ces fonctionnalités. Google recommande d'utiliser NavigationBar pour les nouveaux projets et de migrer les projets existants.

Pourquoi l'écran est-il recréé en cliquant sur un élément BottomNavigation ?

Le problème est l'absence de saveState et restoreState dans navigate(). Sans eux, NavController ne sauvegarde pas l'état de l'onglet précédent. Ajoutez popUpTo(startDestination) { saveState = true } et restoreState = true dans NavOptions pour préserver LazyColumn, la position de défilement et les champs de saisie.

Comment personnaliser l'animation de changement entre les onglets ?

L'animation de changement est contrôlée au niveau de NavHost, pas de BottomNavigation. Ajoutez composable(route, enterTransition, exitTransition) avec des animations personnalisées. Pour BottomNavigation, une animation de fondu (fadeIn + fadeOut) d'une durée de 300 ms est typique.

Résumé

  • BottomNavigation (NavigationBar dans M3) — barre inférieure pour basculer entre 3 à 5 écrans principaux de l'application
  • NavigationBarItem — élément du panneau avec icône, libellé, badge et état de sélection
  • Intégration avec NavController nécessite popUpTo, launchSingleTop et restoreState pour une gestion correcte de la pile
  • NavigationBar de Material3 prend en charge la Couleur Dynamique, BadgedBox, WindowInsets et l'animation de l'élément actif
  • BadgedBox — composant pour les badges de notification avec un nombre ou un point sur l'icône de l'élément
  • État de sélection est calculé à partir de currentBackStackEntryAsState pour la synchronisation avec NavController
  • Pour masquer la barre sur les écrans enfants, utilisez le rendu conditionnel dans le bottomBar de Scaffold basé sur une liste de routes

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi