ViewModel 使用 JetpackCompose 触发导航

eim*_*mer 5 android android-jetpack-compose jetpack-compose-navigation compose-recomposition

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

最佳实践是从 ViewModel 内触发导航吗?是否有有意的机制来触发可组合项中的导航以响应 ViewModel 状态更改?

使用 Jetpack Compose 处理此用例的过程并不明显。如果我尝试类似以下示例的操作,将会发生导航,但我导航到的目的地将无法正常运行。我相信这是因为在调用导航之前不允许完成原始的可组合函数。

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

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

// 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

它是否正确?LaunchedEffect 的文档说明如下,但我并不是 100% 清楚其含义:

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

z.g*_*g.y 1

这段代码

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

其行为不正确很可能会导致这样的问题解决该问题的方法之一就是这样做

// 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我建议不仅根据建议的组件考虑“正确”,还要考虑如何避免像我提供的链接这样的导航陷阱。

如果它来自某个或某些流排放并不重要ViewModel,但组合中安全导航的想法(据我所知)是确保导航调用仅发生在永远不会重新生成的块中执行成功re-compositions也受到上面第一种代码的影响。