Mastering Navigation in Android Compose UI — Integrating Bottom Navigation and Non-Bottom Menu Screens

Thulasi Rajan P
4 min readMay 25, 2024

--

Navigation flow of the app with and without bottom menu — in single navigation graph.

Problem statement :-

I have been learning compose since last few months, and in one of the recent projects there was a UI design with the above mentioned flow (i.e) with and without bottom menus. And I wanted to implement it with Single Navigation Graph as the app has so minimal features and there is no specific requirement to use nested graph. (personally I thought…). So Let’s dive into the detailed steps you’ll need to follow in case you need to implement something like this.

Project setup :-

First create an empty Compose project in Android Studio. And then in the build.gradle file add the below dependencies inside the dependencies block

implementation("androidx.compose.material:material:1.6.7")
implementation("androidx.navigation:navigation-compose:2.7.7")

Creating the screens :-

Create a Kotlin file named as AppScreens.kt , In this file let’s create a sealed class to declare the Screens.

sealed class AppScreens(
val route: String,
val label: String,
val drawable: ImageVector? = null
) {
//direct screens
data object Splash : AppScreens(route = "splash", label = "SplashScreen")
data object Notifications : AppScreens(route = "notifications", label = "Notifications")
data object HomeToNextScreen : AppScreens(route = "homeToNext", label = "HomeToNextScreen")

//bottom menu screens
data object Home : AppScreens(
route = "home",
label = "Home",
drawable = Icons.Default.Home
)

data object Orders : AppScreens(
route = "orders",
label = "Orders",
drawable = Icons.Default.List
)

data object Profile : AppScreens(
route = "profile",
label = "Profile",
drawable = Icons.Default.AccountCircle
)
}

val bottomMenuScreens = listOf(
AppScreens.Home.route,
AppScreens.Profile.route,
AppScreens.Orders.route
)

In the above code, I have declared the screens that we will be using.

Creating Navigation Graph:

Lets create a navigation graph, Before that I wanted to say that the bottom menu is a part of Activity always if we need to make a single navigation and also I found that this approach works well, If you have any other ideas let me know in the comments.

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

val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
val canShowBottomBar = currentRoute in bottomMenuScreens

Scaffold(
bottomBar = {
if (canShowBottomBar) {
BuildBottomNavigationBar(navController, currentRoute)
}
},
content = { paddingValues ->
Box(
modifier = Modifier.padding(
bottom = paddingValues.calculateBottomPadding()
)
) {
NavHost(
navController = navController,
startDestination = AppScreens.Splash.route
) {
composable(route = AppScreens.Splash.route) {
SplashScreen(navController)
}
composable(route = AppScreens.Home.route) {
HomeScreen(navController)
}
composable(route = AppScreens.Orders.route) {
OrdersScreen(navController)
}
composable(route = AppScreens.Profile.route) {
ProfileScreen(navController)
}
composable(route = AppScreens.Notifications.route) {
NotificationScreen(navController)
}
composable(route = AppScreens.HomeToNextScreen.route) {
HomeToNextScreen(navController)
}
}
}
}
)
}

This navigation graph, will be directly fed to the content of the activity as below.

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

Now let’s construct the bottom menu, I have created a composable for that and that looks like below.,

@Composable
fun BuildBottomNavigationBar(
navController: NavHostController,
currentRoute: String?
) {
val bottomMenus = listOf(
AppScreens.Home,
AppScreens.Orders,
AppScreens.Profile
)

BottomAppBar(
modifier = Modifier.fillMaxWidth(),
content = {
bottomMenus.forEach { item ->
BottomNavigationItem(
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
},
icon = { Icon(imageVector = item.drawable!!, contentDescription = item.label) },
label = { Text(item.label) },
selectedContentColor = Color.Black,
unselectedContentColor = Color.Gray,
)
}
}
)
}

FYI, I’m including the git repo here so that you can see the code, Git link:

GitHub — itsgeniuS/compose_single_graph_navigation: Compose Android App with bottom menu and other…

Compose Android App with bottom menu and other single screens with single navigation graph. …github.com

Navigation implementation :

  1. Direct Navigation to screen : For the screens without bottom menu we can directly call like below,
Button(
onClick = {
navController.navigate(AppScreens.Home.route)
},
modifier = Modifier.padding(top = 10.dp)
) {
Text(text = "Open home")
}

2. Bottom Navigation item to screen : For the bottom menus, we will pop the previous screens and we will launch the new screen inside the same container as below,

BottomNavigationItem(
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
},
icon = { Icon(imageVector = item.drawable!!, contentDescription = item.label) },
label = { Text(item.label) },
selectedContentColor = Color.Black,
unselectedContentColor = Color.Gray,
)

Conclusion:

Implementing single navigation graph with this approach has been never easy as far as I felt. By following this guide, you should be well-equipped to add a Bottom Navigation Bar to your next Jetpack Compose project.

Happy Coding ✌️ I Hope this helps you in any part of the work, Thanks guys. See you later…

--

--

Thulasi Rajan P

Android — Kotlin, Java | Flutter — android, iOS & web | Freelancer | More about me — https://itsgenius.github.io/