我正在尝试将Jetpack Compose导航应用到我的应用程序中。
我的屏幕:登录/注册屏幕和底部导航栏屏幕(呼叫、聊天、设置)。
我已经发现,最好的方法是使用嵌套图。
但是我一直得到ViewModelStore should be set before setGraph call
异常。但是,我认为这不是正确的异常。
我的导航已经是最新版本了。可能是我的嵌套图逻辑不正确。
要求:我希望能够从登录或注册屏幕导航到任何BottomBar屏幕并反向
@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
6条答案
按热度按时间bbuxkriu1#
在这个问题上挣扎了一段时间后,我通过使用两个独立的NavHost解决了这个问题。这可能不是正确的方法,但目前它是有效的。你可以在这里找到示例源代码:
https://github.com/talhaoz/JetPackCompose-LoginAndBottomBar
希望他们在即将发布的版本中使导航更容易。
dffbzjpn2#
不允许嵌套NavHost。这会导致ViewModelStore应在setGraph调用异常之前设置。通常,底部导航位于NavHost之外,这是docs显示的内容。推荐的方法是使用单个NavHost,您可以在其中根据您所在的目标隐藏和显示底部导航。
qc6wkl3g3#
一个NavHost、一个NavHostController。在AppScaffold上的巢状NavHost前面建立新的NavHostController。
e37o9pze4#
在实现此通用UI模式时也有类似问题:
1.主页(带有底部导航栏),此页面由
Inner nav controller
托管1.单击一个页面的一些链接
1.导航到一个新页面(包含新的Scaffold示例)。此页面由
Outer nav controller
托管。通过使用2个NavHost和2个navController示例,有点 * 破解 * 了这个问题。
基本思想是使用一些msg通道来告诉
outer nav controller
,在我的例子中是Channel
。缺点:
1.应用程序需要维护UI堆栈信息。
1.响应式布局更难科普。
ki0zmccv5#
为您的函数使用rememberNavController()
5sxhfpxr6#
在我的情况下,我必须创建导航控制器(为底部酒吧)与在主屏幕。