![](/img/trans.png)
[英]What is the proper way to navigate between screens in jetpack compose while there's coroutines jobs to be executed in the viewmodel?
[英]What is the proper way to navigate from ViewModel in Jetpack Compose + Hilt + ViewModel?
我偶然发现了这个非常微不足道但很棘手的问题。 我花了相当多的时间搜索官方文档,但不幸的是没有找到答案。
官方文档说您应该将NavController
的实例传递给@Composable
-s,并将其称为onClick = { navController.navigate("path") }
。 但是,如果我必须从 ViewModel 触发导航事件(例如登录时重定向、重定向到新创建的帖子页面)会发生什么? 在@Composable
中等待任何协程(例如 HTTP 请求)不仅不好,而且可能会因为阻塞的 UI 线程而迫使 Android 杀死应用程序
非官方解决方案(主要以 Medium 文章的形式记录)基于具有单例类并观察一些MutableStateFlow
包含路径的概念。
这在理论上听起来很愚蠢,在实践中并没有多大帮助(对副作用和重组不友好,会触发不必要的重新导航)。
rememberNavController
有一个非常简单的源代码,您可以使用它在单例服务中创建它:
@Singleton
class NavigationService @Inject constructor(
@ApplicationContext context: Context,
) {
val navController = NavHostController(context).apply {
navigatorProvider.addNavigator(ComposeNavigator())
navigatorProvider.addNavigator(DialogNavigator())
}
}
创建一个辅助视图模型以与NavHost
视图共享NavHostController
:
@HiltViewModel
class NavViewModel @Inject constructor(
navigationService: NavigationService,
): ViewModel() {
val controller = navigationService.navController
}
NavHost(
navController = hiltViewModel<NavViewModel>().controller,
startDestination = // ...
) {
// ...
}
然后在任何视图模型中,您都可以将其注入并用于导航:
@HiltViewModel
class ScreenViewModel @Inject constructor(
private val navigationService: NavigationService
): ViewModel() {
fun navigateToNextScreen() {
navigationService.navController.navigate(Destinations.NextScreen)
}
}
我自己也一直在为同样的问题而苦苦挣扎。 从谷歌提供的关于这个主题的有限文档, 特别是架构事件部分,我想知道他们是否建议使用状态作为导航的触发器?
引用文件:
例如,在实现登录屏幕时,点击登录按钮应该会导致您的应用显示进度微调器和网络调用。 如果登录成功,那么您的应用会导航到另一个屏幕; 如果出现错误,应用程序会显示 Snackbar。 以下是对屏幕状态和事件建模的方法:
他们为上述要求提供了以下代码片段:
sealed class UiState {
object SignedOut : UiState()
object InProgress : UiState()
object Error : UiState()
object SignIn : UiState()
}
class MyViewModel : ViewModel() {
private val _uiState = mutableStateOf<UiState>(SignedOut)
val uiState: State<UiState>
get() = _uiState
}
他们没有提供的是其余的视图模型和编写代码。 我猜它应该看起来像:
@Composable
fun MyScreen(navController: NavController, viewModel: MyViewModel) {
when(viewModel.uiState){
is SignedOut -> // Display signed out UI components
is InProgress -> // Display loading spinner
is Error -> // Display error toast
// Using the SignIn state as a trigger to navigate
is SignIn -> navController.navigate(...)
}
}
视图模型也可以具有这样的功能(通过单击撰写屏幕上的“登录”按钮触发
fun onSignIn() {
viewModelScope.launch {
// Make a suspending sign in network call
_uiState.value = InProgress
// Trigger navigation
_uiState.value = SignIn
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.