DEV Community

Cover image for How to Add Bottom Navigation in Jetpack Compose?
Vincent Tsen
Vincent Tsen

Posted on • Edited on • Originally published at vtsen.hashnode.dev

How to Add Bottom Navigation in Jetpack Compose?

Step-by-step guides to add bottom navigation in Jetpack Compose for beginners

This is part of the Jetpack Compose navigation series:

This article shows the steps that you need to do to add the bottom navigation from this simple navigation in Jetpack Compose example in part 1.

How_to_Add_Bottom_Navigation _in_Jetpack_Compose_00.gif

1. Add Icon Vector Asset

In this example, you're adding the ic_home.xml and ic_search.xml vector assets for the screen navigation tab.

This highlights the steps :

  • New -> ** Vector Asset**
  • Click on the Clip Art to select the Icon asset
  • Rename it

How_to_Add_Bottom_Navigation _in_Jetpack_Compose_01.gif

You may get the following compilation error after adding those icons.

AAPT: error: resource attr/colorControlNormal not found.
Enter fullscreen mode Exit fullscreen mode

It is because of the dependency on the androidx.appcompat:appcompat library, which is not required by Jetpack Compose app.

To fix the error, add this library into your app\build.gradle file:

dependencies {
    // required by "?attr/colorControlNormal" ic_home.xml
    implementation 'androidx.appcompat:appcompat:1.4.1'
}
Enter fullscreen mode Exit fullscreen mode

[Updated - Sept 27, 2022]: It looks like this step is unnecessary because we can just reference ImageVector directly from the code using Icons.Default.Home. So this dependency implementation 'androidx.appcompat:appcompat:1.4.1' can be removed as well.

2. Add BottomNavigation() Composable

androidx.compose.material.BottomNavigation() composable function is used to implement the bottom bar navigation.

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

    if (currentRoute == null || currentRoute == NavRoute.Login.path) {
        return
    }

    BottomNavigation {
        // implement each navigation tab with BottomNavigationItem() here
    }
}
Enter fullscreen mode Exit fullscreen mode

navController.currentBackStackEntryAsState() is used so that it can retrigger the composable function to run when the navigation route is changed.

3. Add BottomNavigationItem() Composable

In the BottomNavigation(), you add the RowScope.BottomNavigationItem() for each row navigation tab.

BottomNavigation {

    val homeSelected = currentRoute == NavRoute.Home.path
    BottomNavigationItem(
        icon = {
            Icon(
                painter = painterResource(R.drawable.ic_home),
                contentDescription = NavRoute.Home.path
            )
        },
        selected = homeSelected,
        onClick = {
            if(!homeSelected) {
                navController.navigate(NavRoute.Home.path) {
                    popUpTo(NavRoute.Home.path) { inclusive = true }
                }
            }
        },
        label = {Text(NavRoute.Home.path)}
    )
}
Enter fullscreen mode Exit fullscreen mode

icon, selected and onClick are mandatory parameters, the rest are optional.

[Updated - Sept 27, 2022]: Since importing icon asset manually (see above section) is no longer needed, you replace

Icon(
    painter = painterResource(R.drawable.ic_home),
    contentDescription = NavRoute.Home.path
)
Enter fullscreen mode Exit fullscreen mode

with

Icon(
    imageVector = Icons.Default.Home,
    contentDescription = NavRoute.Home.path
)
Enter fullscreen mode Exit fullscreen mode

The commit changes are here.

4. Implement Scaffold bottomBar

To add BottomBarNavigation(), you call it from the Scaffold -> bottomBar parameter as the following:

val navController = rememberNavController()

Scaffold(
    bottomBar = { BottomBarNavigation(navController = navController) }
) {
    NavGraph(navController)
}
Enter fullscreen mode Exit fullscreen mode

[Updated - Sept 28, 2022]: While working on this Simple RSS Feed reader app, I noticed that the content is covered by this bottom bar navigation. To fix that, I used
PaddingValues.calculateBottomPadding() from the Scaffold() to add the bottom padding of the content.

  • Use paddingValues.calculateBottomPadding() to calculate the required bottom bar padding
val navController = rememberNavController()

Scaffold(
    bottomBar = { BottomBarNav(navController = navController) }
) { paddingValues ->
    NavGraph(
        modifier = Modifier.padding(
            bottom = paddingValues.calculateBottomPadding()),
        navController = navController
    )
}
Enter fullscreen mode Exit fullscreen mode
  • Add Modifier parameter into NavGraph() and NavHost()
@Composable
fun NavGraph(
    modifier: Modifier = Modifier, 
    navController: NavHostController) {

    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = NavRoute.Login.path
    ) {
        /*...*/
    }
}

Enter fullscreen mode Exit fullscreen mode

Source Code

GitHub Repository: Demo_SimpleNavigationCompose (bottom_nav branch)


Originally published at https://vtsen.hashnode.dev.

Top comments (0)