Bottom Navigation in Jetpack Compose: A Complete Integration Guide

Jetpack Compose is Google’s modern UI toolkit for building native Android apps. It provides a declarative way to design user interfaces, making development faster and more intuitive. One common UI pattern in mobile apps is bottom navigation, which allows users to quickly switch between top-level views or features. Integrating bottom navigation in Jetpack Compose involves using the BottomNavigation and BottomNavigationItem composables.

What is Bottom Navigation?

Bottom navigation is a UI component that appears at the bottom of a mobile app screen. It typically consists of several icons or text labels that represent different sections or features of the app. When a user taps on an item in the bottom navigation, the app navigates to the corresponding screen or view.

Why Use Bottom Navigation?

  • Improved User Experience: Provides quick access to essential features.
  • Intuitive Navigation: Simplifies app navigation with clear visual cues.
  • Enhanced Accessibility: Easier to reach than top navigation, especially on larger screens.

How to Implement Bottom Navigation in Jetpack Compose

Implementing bottom navigation in Jetpack Compose involves the following steps:

Step 1: Add Dependencies

Ensure you have the necessary dependencies in your build.gradle file. You’ll need Compose UI and Navigation dependencies:

dependencies {
    implementation("androidx.compose.ui:ui:1.6.4")
    implementation("androidx.compose.material:material:1.6.4")
    implementation("androidx.navigation:navigation-compose:2.7.7")
    implementation("androidx.compose.ui:ui-tooling-preview:1.6.4")
    debugImplementation("androidx.compose.ui:ui-tooling:1.6.4")
}

Step 2: Define Navigation Items

Create data classes to represent each item in the bottom navigation:

import androidx.compose.ui.graphics.vector.ImageVector

data class BottomNavItem(
    val name: String,
    val route: String,
    val icon: ImageVector,
    val badgeCount: Int = 0
)

Step 3: Create the BottomNavigation Composable

Implement the BottomNavigation composable with BottomNavigationItem for each navigation item:


import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState

@Composable
fun BottomNavigationBar(navController: NavController, items: List<BottomNavItem>) {
    BottomNavigation(
        backgroundColor = Color.White,
        contentColor = Color.Black
    ) {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route

        items.forEach { item ->
            BottomNavigationItem(
                icon = { Icon(imageVector = item.icon, contentDescription = item.name) },
                label = {
                    Text(
                        text = item.name,
                        fontSize = 9.sp
                    )
                },
                selectedContentColor = Color.Black,
                unselectedContentColor = Color.Gray,
                alwaysShowLabel = true,
                selected = currentRoute == item.route,
                onClick = {
                    navController.navigate(item.route) {
                        navController.graph?.startDestinationRoute?.let { route ->
                            popUpTo(route) {
                                saveState = true
                            }
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                }
            )
        }
    }
}

Step 4: Define Navigation Graph

Set up the navigation graph using NavController and NavHost:


import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Email
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    val items = listOf(
        BottomNavItem(
            name = "Home",
            route = "home",
            icon = Icons.Default.Home
        ),
        BottomNavItem(
            name = "Chat",
            route = "chat",
            icon = Icons.Default.Email,
            badgeCount = 23
        ),
        BottomNavItem(
            name = "Settings",
            route = "settings",
            icon = Icons.Default.Settings
        )
    )

    Scaffold(
        bottomBar = {
            BottomNavigationBar(
                navController = navController,
                items = items
            )
        }
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
        ) {
            // Add padding here or handle it in individual screen composables
            NavHost(navController = navController, startDestination = "home") {
                composable("home") {
                    HomeScreen()
                }
                composable("chat") {
                    ChatScreen()
                }
                composable("settings") {
                    SettingsScreen()
                }
            }
        }
    }
}

@Composable
fun HomeScreen() {
    Text(text = "Home Screen")
}

@Composable
fun ChatScreen() {
    Text(text = "Chat Screen")
}

@Composable
fun SettingsScreen() {
    Text(text = "Settings Screen")
}

Step 5: Add Screens

Create composable functions for each screen or feature in the bottom navigation:


@Composable
fun HomeScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Home Screen", fontSize = 24.sp)
    }
}

@Composable
fun ChatScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Chat Screen", fontSize = 24.sp)
    }
}

@Composable
fun SettingsScreen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Settings Screen", fontSize = 24.sp)
    }
}

Step 6: Preview and Test

Use the @Preview annotation to preview the bottom navigation in the IDE and test the navigation on an emulator or physical device.


@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MainScreen()
}

Conclusion

Integrating bottom navigation in Jetpack Compose provides an intuitive and efficient way to navigate between different sections of your Android app. By defining navigation items, implementing the BottomNavigation composable, setting up the navigation graph, and adding screen composables, you can create a seamless and user-friendly navigation experience. Jetpack Compose’s declarative approach simplifies the process and allows for easy customization to match your app’s design and functionality. Proper use of bottom navigation can significantly enhance the overall user experience and app engagement.