I am trying to apply Jetpack Compose navigation into my application.
My Screens: Login/Register screens and Bottom navbar screens(call, chat, settings).
I already found out that the best way to do this is using nested graphs.
But I keep getting ViewModelStore should be set before setGraph call
exception. However, I don't think this is the right exception.
My navigation is already in the latest version. Probably my nested graph logic is not right.
Requirement: I want to be able to navigate from the Login or Register screen to any BottomBar Screen & reverse
@Composable
fun SetupNavGraph(
navController: NavHostController,
userViewModel: UserViewModel
) {
NavHost(
navController = navController,
startDestination = BOTTOM_BAR_GRAPH_ROUTE,
route = ROOT_GRAPH_ROUTE
) {
loginNavGraph(navController = navController, userViewModel)
bottomBarNavGraph(navController = navController, userViewModel)
}
}
NavGraph.kt
fun NavGraphBuilder.loginNavGraph(
navController: NavHostController,
userViewModel: UserViewModel
) {
navigation(
startDestination = Screen.LoginScreen.route,
route = LOGIN_GRAPH_ROUTE
) {
composable(
route = Screen.LoginScreen.route,
content = {
LoginScreen(
navController = navController,
loginViewModel = userViewModel
)
})
composable(
route = Screen.RegisterScreen.route,
content = {
RegisterScreen(
navController = navController,
loginViewModel = userViewModel
)
})
}
}
LoginNavGraph.kt
fun NavGraphBuilder.bottomBarNavGraph(
navController: NavHostController,
userViewModel: UserViewModel
) {
navigation(
startDestination = Screen.AppScaffold.route,
route = BOTTOM_BAR_GRAPH_ROUTE
) {
composable(
route = Screen.AppScaffold.route,
content = {
AppScaffold(
navController = navController,
userViewModel = userViewModel
)
})
}
}
BottomBarNavGraph.kt
@Composable
fun AppScaffold(
navController: NavHostController,
userViewModel: UserViewModel
) {
val scaffoldState = rememberScaffoldState()
Scaffold(
bottomBar = {
BottomBar(mainNavController = navController)
},
scaffoldState = scaffoldState,
) {
NavHost(
navController = navController,
startDestination = NavigationScreen.EmergencyCallScreen.route
) {
composable(NavigationScreen.EmergencyCallScreen.route) {
EmergencyCallScreen(
navController = navController,
loginViewModel = userViewModel
)
}
composable(NavigationScreen.ChatScreen.route) { ChatScreen() }
composable(NavigationScreen.SettingsScreen.route) {
SettingsScreen(
navController = navController,
loginViewModel = userViewModel
)
}
}
}
}
AppScaffold.kt
@Composable
fun BottomBar(mainNavController: NavHostController) {
val items = listOf(
NavigationScreen.EmergencyCallScreen,
NavigationScreen.ChatScreen,
NavigationScreen.SettingsScreen,
)
BottomNavigation(
elevation = 5.dp,
) {
val navBackStackEntry by mainNavController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.map {
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = it.icon),
contentDescription = it.title
)
},
label = {
Text(
text = it.title
)
},
selected = currentRoute == it.route,
selectedContentColor = Color.White,
unselectedContentColor = Color.White.copy(alpha = 0.4f),
onClick = {
mainNavController.navigate(it.route) {
mainNavController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
restoreState = true
launchSingleTop = true
}
},
)
}
}
}
BottomBar.kt
const val ROOT_GRAPH_ROUTE = "root"
const val LOGIN_GRAPH_ROUTE = "login_register"
const val BOTTOM_BAR_GRAPH_ROUTE = "bottom_bar"
sealed class Screen(val route: String) {
object LoginScreen : Screen("login_screen")
object RegisterScreen : Screen("register_screen")
object AppScaffold : Screen("app_scaffold")
}
Screen.kt
sealed class NavigationScreen(val route: String, val title: String, @DrawableRes val icon: Int) {
object EmergencyCallScreen : NavigationScreen(
route = "emergency_call_screen",
title = "Emergency Call",
icon = R.drawable.ic_phone
)
object ChatScreen :
NavigationScreen(
route = "chat_screen",
title = "Chat",
icon = R.drawable.ic_chat)
object SettingsScreen : NavigationScreen(
route = "settings_screen",
title = "Settings",
icon = R.drawable.ic_settings
)
}
NavigationScreen.kt
NavController
must be associated with a singleNavHost
composable. To fix that issue you need to pass new instance ofrememberNavController()
. developer.android.com/jetpack/compose/navigation#create-navhost – Keifer