Skip to content

Jetpack Compose Bottom Navigation — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Jetpack Compose Bottom Navigation. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your NavigationBar items don't highlight when selected, the badge never shows, or clicking an item navigates to the wrong screen.

Wrong Approach ❌

@Composable
fun BadBottomNav() {
    val items = listOf("Home", "Search", "Profile")
    var selected by remember { mutableStateOf("") }

    NavigationBar {
        items.forEach { label ->
            NavigationBarItem(
                selected = selected == label,
                onClick = { selected = label }, // No navigation!
                icon = { Icon(Icons.Default.Home, "") },
                label = { Text(label) }
            )
        }
    }
}

Output: Fake selection without actual navigation. All items show the same icon.

Right Approach ✅

sealed class BottomNavItem(val route: String, val icon: ImageVector, val label: String) {
    object Home : BottomNavItem("home", Icons.Default.Home, "Home")
    object Search : BottomNavItem("search", Icons.Default.Search, "Search")
    object Profile : BottomNavItem("profile", Icons.Default.Person, "Profile")
}

@Composable
fun GoodBottomNav(navController: NavHostController) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route

    NavigationBar {
        val items = listOf(BottomNavItem.Home, BottomNavItem.Search, BottomNavItem.Profile)
        items.forEach { item ->
            NavigationBarItem(
                selected = currentRoute == item.route,
                onClick = {
                    navController.navigate(item.route) {
                        popUpTo(navController.graph.startDestinationId) { saveState = true }
                        launchSingleTop = true
                        restoreState = true
                    }
                },
                icon = {
                    if (item == BottomNavItem.Home && unreadCount > 0) {
                        BadgedBox(badge = { Badge { Text("$unreadCount") } }) {
                            Icon(item.icon, item.label)
                        }
                    } else {
                        Icon(item.icon, item.label)
                    }
                },
                label = { Text(item.label) }
            )
        }
    }
}

Output: Proper navigation with badge support and state restoration.

Prevention

  • Use currentBackStackEntryAsState() for reactive selection tracking.
  • Use popUpTo + saveState/restoreState for proper back stack behavior.
  • Use BadgedBox for notification badges.
  • Define nav items as sealed objects for type safety.

Common Mistakes with compose bottom nav

  1. Using foldl instead of foldl' causing stack overflow on large lists
  2. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  3. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### How do I prevent the bottom bar from recomposing on every nav change?

Use saveState and restoreState in navController.navigate(). This avoids re-creating composables when switching tabs.

### What is the difference between NavigationBar (M3) and BottomNavigation (M2)?

NavigationBar is the Material 3 component with updated styling, larger touch targets, and built-in container color support. Use NavigationBar for Material 3 projects.

### Can I combine bottom nav with nested navigation?

Yes. Each bottom nav item can have its own NavHost (nested nav graph). Use NavHost with startDestination per tab.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro