Compose 副作用 + Jetpack 导航 + onBackPressed = 卡住导航

No *_*eto 7 android side-effects android-jetpack-compose jetpack-compose-navigation

我遇到这个问题,当异步任务执行后给定状态更新时,我必须进行导航。我这样做是这样的:

At ViewModel.kt
fun executeRandomTask() {
    viewModelScope.launch {
        runAsyncTask()
        state = Success
    }
}

At Composable.kt
LaunchedEffect(viewModel.state) {
    if(viewModel.state is Success) {
        navController.navigate("nextScreen")
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在下一个屏幕中,我单击后退导航按钮 (onBackPressed),会发生什么情况,效果再次启动。所以我又回到了“nextScreen”。

当我执行下一个解决方法时:

DisposableEffect(viewModel.state) {
    if(viewModel.state is Success) {
        navController.navigate("nextScreen")
    }
    onDispose {
        viewModel.state = null 
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样,视图模型状态被清除,它也证明正在发生的事情是导航控​​制器破坏了前一个屏幕(不确定这是否是预期的行为)。

我不确定我应该做什么来避免这种情况,因为这是一种非常常见的情况,并且在达到某个状态后必须清除状态看起来很脏。

小智 3

我使用 SharedFlow 来发出这样的一次性事件

class MyViewModel : ViewModel() {

    private val _eventFlow = MutableSharedFlow<OneTimeEvent>()
    val eventFlow = _eventFlow.asSharedFlow()

    private fun emitEvent(event: OneTimeEvent) {
        viewModelScope.launch { _eventFlow.emit(event) }
    }

}
Run Code Online (Sandbox Code Playgroud)

用于定义事件的密封类

sealed class OneTimeEvent {
    object SomeEvent: OneTimeEvent()
}
Run Code Online (Sandbox Code Playgroud)

最后在屏幕中收集这样的流程

fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    LaunchedEffect(Unit) {
        viewModel.eventFlow.collect { event ->
            when(event){
                SomeEvent -> { 
                    //Do Something 
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,每当您的状态发生变化时,您都可以发出一些事件并在屏幕中对其采取操作。