ViewModel通过JetpackCompose触发导航



在Android中,我经常想导航是对ViewModel状态更改的响应。(例如,成功的身份验证触发导航到用户的主屏幕。)

从ViewModel中触发导航的最佳做法是什么?是否有一种有意的机制来触发可组合中的导航以响应ViewModel状态的更改?

使用JetpackCompose,处理这个用例的过程并不明显。如果我尝试类似以下示例的操作,将会出现导航,但我导航到的目的地将无法正常工作。我相信这是因为最初的可组合函数在调用导航之前是不允许完成的。

// Does not behave correctly.
@Composable fun AuthScreen() {
val screenState = viewModel.screenState.observeAsState()
if(screenState.value is ScreenState.UserAuthenticated){
navController.navigate("/gameScreen")
} else {
LoginScreen()
}
}

如果我使用LauncedEffect,我确实观察到了正确的行为,如下所示:

// Does behave correctly.
@Composable fun AuthScreen() {
val screenState = viewModel.screenState.observeAsState()
if(screenState.value is ScreenState.UserAuthenticated){
LaunchedEffect(key1 = "test") {
navController.navigate("$/gameScreen")
}
} else {
LoginScreen()
}
}

这是正确的吗?LaunchedEffect的文档说明了以下内容,但我并不完全清楚其含义:

当LaunchedEffect进入合成时,它将在合成的CoroutineContext中启动块。当LaunchedEffect用不同的key1、key2或key3重新组合时,协同程序将被取消并重新启动。当LaunchedEffect离开合成时,协同程序将被取消。

此代码为

// Does not behave correctly.
@Composable fun AuthScreen() {
val screenState = viewModel.screenState.observeAsState()
if(screenState.value is ScreenState.UserAuthenticated){
navController.navigate("/gameScreen")
} else {
LoginScreen()
}
}

行为不正确最有可能导致这样的问题,解决问题的方法之一是通过

// Does behave correctly.
@Composable fun AuthScreen() {
val screenState = viewModel.screenState.observeAsState()
if(screenState.value is ScreenState.UserAuthenticated){
LaunchedEffect(key1 = "test") {
navController.navigate("$/gameScreen")
}
} else {
LoginScreen()
}
}

它的行为是正确的,因为假设key在下一次组合过程中不会改变,则保证LaunchedEffect每个composition只执行一次,否则它将在其可组合范围的每次更新时继续执行。

我建议考虑";正确的";不仅基于建议的组件,还考虑如何避免导航陷阱,比如我提供的链接。

它是来自ViewModel还是一些流量排放并不重要,但compose中安全导航的想法(据我所知)是确保导航调用只发生在一个块中,该块将永远不会在后续re-compositions上重新执行,这一块也受到上面第一类代码的影响。

最新更新