Android Jetpack Compose BottomNavigationBar With Compose Navigation

October 29, 2021

Include dependencies in Modules’s build.gradle

dependencies {
  implementation 'androidx.navigation:navigation-compose:2.4.0-alpha10'
}

Activity

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.luasoftware.journey.ui.theme.JourneyTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JourneyApp()
        }
    }
}

Screens

sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
    object Home : Screen("home", "Home", Icons.Filled.Home)
    object Collections : Screen("collection", "Collections", Icons.Filled.List)
    object UserProfile : Screen("user_profile", "Profile", Icons.Filled.AccountCircle)
}

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

@Composable
fun CollectionsScreen() {
    Text(text = "Collections")
}

@Composable
fun UserProfileScreen() {
    Text(text = "Profile")
}

Main Compose

@Composable
fun JourneyApp() {
    JourneyTheme {
        val navController = rememberNavController()
        val screens = listOf(
            Screen.Home,
            Screen.Collections,
            Screen.UserProfile
        )
        // https://developer.android.com/jetpack/compose/navigation
        Scaffold(
            bottomBar = {
                BottomNavigation {
                    val navBackStackEntry by navController.currentBackStackEntryAsState()
                    val currentDestination = navBackStackEntry?.destination
                    screens.forEach { screen ->
                        BottomNavigationItem(
                            icon = { Icon(imageVector = screen.icon, contentDescription = screen.label) },
                            label = { Text(screen.label) },
                            selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
                            onClick = {
                                navController.navigate(screen.route) {
                                    // Pop up to the start destination of the graph to
                                    // avoid building up a large stack of destinations
                                    // on the back stack as users select items
                                    popUpTo(navController.graph.findStartDestination().id) {
                                        saveState = true
                                    }
                                    // Avoid multiple copies of the same destination when
                                    // reselecting the same item
                                    launchSingleTop = true
                                    // Restore state when reselecting a previously selected item
                                    restoreState = true
                                }
                            }
                        )

                    }
                }
            }
        ) { innerPadding ->
            Box(modifier = Modifier.padding(innerPadding)) {
                // nav graph
                NavHost(navController = navController, startDestination = Screen.Home.route) {
                    composable(Screen.Home.route) { HomeScreen() }
                    composable(Screen.Collections.route) { CollectionsScreen() }
                    composable(Screen.UserProfile.route) { UserProfileScreen() }
                }
            }
        }
    }
}

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.