Implementing Material 3 Bottom Navigation in Jetpack Compose

Jetpack Compose has revolutionized Android development, offering a declarative UI toolkit that simplifies the creation of beautiful, responsive, and dynamic applications. One of the most common design patterns in modern Android apps is bottom navigation—a key component for improving user experience and app navigation. With the introduction of Material 3, developers can now leverage enhanced design principles and updated components to build intuitive bottom navigation systems.

In this blog post, we’ll explore how to implement Material 3 Bottom Navigation in Jetpack Compose, delving into advanced concepts, best practices, and tips for seamless integration.

Why Material 3 for Bottom Navigation?

Material 3 (M3), also known as Material You, introduces a more personalized and flexible design system, emphasizing dynamic color schemes, accessibility, and modern aesthetics. For bottom navigation, Material 3 provides updated components that:

  • Align with Material You’s adaptive theming.

  • Enhance accessibility with better touch targets and labels.

  • Offer advanced customization options for icons, colors, and animations.

Using Material 3’s bottom navigation ensures your app stays modern, visually appealing, and in line with Android’s latest design guidelines.

Setting Up Jetpack Compose with Material 3

Before diving into the implementation, ensure your project is set up with Jetpack Compose and Material 3 dependencies. Add the following dependencies to your build.gradle:

dependencies {
    implementation "androidx.compose.material3:material3:1.1.0"
    implementation "androidx.navigation:navigation-compose:2.5.3"
}

Ensure you’re using a compatible Compose compiler version:

kotlinOptions {
    jvmTarget = "1.8"
}

Core Components of Material 3 Bottom Navigation

Material 3’s bottom navigation in Jetpack Compose primarily revolves around the NavigationBar and NavigationBarItem components. Let’s break them down:

  • NavigationBar: The container for bottom navigation items. It provides the structure and styling.

  • NavigationBarItem: Represents individual items within the navigation bar, including icons, labels, and interaction states.

Building the Bottom Navigation Bar

Below is an example of how to create a basic Material 3 Bottom Navigation using Jetpack Compose:

Step 1: Define Navigation Destinations

Start by creating a sealed class to represent your app’s navigation destinations:

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

val bottomNavItems = listOf(
    Screen.Home,
    Screen.Search,
    Screen.Profile
)

Step 2: Create the NavigationBar

Use the NavigationBar component to define the bottom navigation bar:

@Composable
fun BottomNavigationBar(
    currentRoute: String,
    onItemSelected: (Screen) -> Unit
) {
    NavigationBar {
        bottomNavItems.forEach { screen ->
            NavigationBarItem(
                selected = currentRoute == screen.route,
                onClick = { onItemSelected(screen) },
                icon = {
                    Icon(
                        imageVector = screen.icon,
                        contentDescription = screen.label
                    )
                },
                label = { Text(screen.label) },
                alwaysShowLabel = true
            )
        }
    }
}

Step 3: Integrate Navigation with Navigation-Compose

Jetpack Compose’s navigation-compose library simplifies navigation handling. Define your app’s navigation graph:

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    Scaffold(
        bottomBar = {
            BottomNavigationBar(
                currentRoute = navController.currentBackStackEntry?.destination?.route.orEmpty(),
                onItemSelected = { screen ->
                    navController.navigate(screen.route) {
                        popUpTo(navController.graph.startDestinationId) { saveState = true }
                        launchSingleTop = true
                        restoreState = true
                    }
                }
            )
        }
    ) { innerPadding ->
        NavHost(
            navController = navController,
            startDestination = Screen.Home.route,
            modifier = Modifier.padding(innerPadding)
        ) {
            composable(Screen.Home.route) { HomeScreen() }
            composable(Screen.Search.route) { SearchScreen() }
            composable(Screen.Profile.route) { ProfileScreen() }
        }
    }
}

Step 4: Create Placeholder Screens

For this example, define simple Composable functions for your screens:

@Composable
fun HomeScreen() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Home Screen")
    }
}

@Composable
fun SearchScreen() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Search Screen")
    }
}

@Composable
fun ProfileScreen() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Text("Profile Screen")
    }
}

Advanced Customizations

Dynamic Colors

Material 3 supports dynamic theming based on the user’s wallpaper and system settings. To apply dynamic colors to your navigation bar, ensure your app’s theme supports it:

MaterialTheme(
    colorScheme = dynamicLightColorScheme(context)
) {
    AppNavigation()
}

Animations

To enhance user experience, consider adding animations when transitioning between screens. Jetpack Compose provides the AnimatedNavHost API:

AnimatedNavHost(
    navController = navController,
    startDestination = Screen.Home.route
) {
    composable(Screen.Home.route) { HomeScreen() }
    composable(Screen.Search.route) { SearchScreen() }
    composable(Screen.Profile.route) { ProfileScreen() }
}

Best Practices

  1. Accessibility: Ensure labels are always visible for better accessibility. Use alwaysShowLabel = true unless your app prioritizes minimalism.

  2. State Management: Utilize rememberSaveable for preserving UI state during configuration changes.

  3. Avoid Overcrowding: Limit the number of bottom navigation items to 3-5 for a clean and user-friendly interface.

  4. Testing: Use Compose testing libraries to validate navigation behavior and UI consistency.

Conclusion

Implementing Material 3 Bottom Navigation in Jetpack Compose empowers developers to create modern, visually appealing, and accessible navigation systems. By leveraging components like NavigationBar and NavigationBarItem, combined with Jetpack Compose’s navigation library, you can build a seamless and dynamic user experience.

Start integrating Material 3 into your Jetpack Compose projects today, and take advantage of its adaptability and design consistency to enhance your app’s usability and aesthetics.

Do you have any advanced tips or favorite use cases for Material 3 Bottom Navigation? Share your thoughts in the comments below!