Bottom Navigation in Jetpack Compose Android

This post is about how to add bottom navigation in Jetpack Compose using navigation compose.

 

Creating New Project

1 . Create a new project by going to File ⇒ New Android Project, select Empty Compose Activity, provide app name and then finally click on finish.

2 . Open app-level build.gradle file and under the dependencies section add the below navigation-compose library and then sync the project:

build.gradle

dependencies {
//navigation with compose
implementation("androidx.navigation:navigation-compose:2.4.2")
}

Get the latest navigation-compose dependency from here.

3. Now create a sealed class with the name NavigationItem with bottom navigation item title, item icon, and item route which we will use later for navigation between screens.

NavigationItem.kt

sealed class NavigationItem(var route: String, var icon: ImageVector, var title: String) {
object Home : NavigationItem("home", Icons.Filled.Home, "Home")
object Place : NavigationItem("place", Icons.Filled.Place, "Place")
object Favorites : NavigationItem("favorites", Icons.Filled.Favorite, "Favorites")
}

4. Inside MainActivity.kt file, create three composable functions Home, Place, and Favorites which define the screen’s content.

@Composable
fun Home() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Home,
contentDescription = "home",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

@Composable
fun Place() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Place,
contentDescription = "contacts",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

@Composable
fun Favorites() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "favorites",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

5. Create a composable function with the name NavigationHost in MainActivity.kt which will contain NavHost and the Composable for navigation. 

@Composable
fun NavigationHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = NavigationItem.Home.route
) {
composable(NavigationItem.Home.route) {
Home()
}
composable(NavigationItem.Place.route) {
Place()
}
composable(NavigationItem.Favorites.route) {
Favorites()
}
}
}

6. Create object NavBarItems, and define a BarItems list that contains bottom navigation items.

NavBarItems.kt

object NavBarItems {
val BarItems = listOf(
NavigationItem.Home,
NavigationItem.Place,
NavigationItem.Favorites,
)
}

7. Create a new composable function BottomNavigationBar to define bottom navigation, its item, and handling bottom navigation backstack.

@Composable
fun BottomNavigationBar(navController: NavHostController) {

BottomNavigation {

//observe the backstack
val backStackEntry by navController.currentBackStackEntryAsState()
//observe current route to change the item icon,label when navigated
val currentRoute = backStackEntry?.destination?.route

//bottom nav items we declared
NavBarItems.BarItems.forEach { navItem ->

//place the bottom nav items
BottomNavigationItem(
selected = currentRoute == navItem.route,
onClick = {
//navigate on click
navController.navigate(navItem.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},

//icon of navItem
icon = {
Icon(
imageVector = navItem.icon,
contentDescription = navItem.title
)
},
//label
label = {
Text(text = navItem.title)
},
)
}
}
}

8. Create a new composable function with Scaffold() so that you can define the topBar, bottom bar, and content.

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

Scaffold(
topBar = { TopAppBar(title = { Text("Bottom Navigation Demo") }) },
content = { NavigationHost(navController = navController) },
bottomBar = { BottomNavigationBar(navController = navController) }
)

}

9. At last just call the MainScreen() function from onCreate method of your activity:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeBottomNavigationExpTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainScreen()
}
}
}
}

Complete MainActivity Code:

MainActivity.kt

package com.c1ctech.jetpackcomposebottomnavigationexp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.ui.graphics.Color
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Place
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.c1ctech.jetpackcomposebottomnavigationexp.ui.theme.JetpackComposeBottomNavigationExpTheme

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeBottomNavigationExpTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainScreen()
}
}
}
}
}

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

Scaffold(
topBar = { TopAppBar(title = { Text("Bottom Navigation Demo") }) },
content = { NavigationHost(navController = navController) },
bottomBar = { BottomNavigationBar(navController = navController) }
)

}

@Composable
fun NavigationHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = NavigationItem.Home.route
) {
composable(NavigationItem.Home.route) {
Home()
}
composable(NavigationItem.Place.route) {
Place()
}
composable(NavigationItem.Favorites.route) {
Favorites()
}
}
}

@Composable
fun BottomNavigationBar(navController: NavHostController) {

BottomNavigation {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = backStackEntry?.destination?.route

NavBarItems.BarItems.forEach { navItem ->

BottomNavigationItem(
selected = currentRoute == navItem.route,
onClick = {
navController.navigate(navItem.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},

icon = {
Icon(
imageVector = navItem.icon,
contentDescription = navItem.title
)
},
label = {
Text(text = navItem.title)
},
)
}
}
}

@Composable
fun Home() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Home,
contentDescription = "home",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

@Composable
fun Place() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Place,
contentDescription = "contacts",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

@Composable
fun Favorites() {

Box(
modifier = Modifier.fillMaxSize()
) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "favorites",
tint = Color.Blue,
modifier = Modifier
.size(150.dp)
.align(Alignment.Center)
)
}
}

When you run the app it will look like this:

       

Leave a Reply