Bor*_*vić 14 android kotlin android-jetpack-compose dagger-hilt
我偶然发现了这个非常琐碎但棘手的问题。我花了相当多的时间搜索官方文档,但不幸的是没有找到答案。
官方文档说您应该将 down 的实例传递NavController给@Composable-s ,并将其称为onClick = { navController.navigate("path") }。但是,如果我必须从 ViewModel 触发导航事件(例如登录时重定向、重定向到新创建的帖子页面),会发生什么情况?等待任何协程(例如 HTTP 请求)@Composable不仅不好,而且可能会因为 UI 线程被阻塞而迫使 Android 终止应用程序
非官方解决方案(主要以 Medium 文章的形式记录)基于具有单例类并观察某些MutableStateFlow包含路径的概念。
这在理论上听起来很愚蠢,并且在实践中没有多大帮助(副作用和重组不友好,会触发不必要的重新导航)。
小智 9
我自己也一直在为同样的问题而苦苦挣扎。从谷歌提供的关于这个主题的有限文档,特别是架构事件部分,我想知道他们是否建议使用状态作为导航的触发器?
引用文档:
例如,在实现登录屏幕时,点击登录按钮应该会使您的应用程序显示进度旋转器和网络调用。如果登录成功,那么您的应用程序将导航到不同的屏幕;如果出现错误,应用程序会显示 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
}
Run Code Online (Sandbox Code Playgroud)
他们没有提供视图模型和组合代码的其余部分。我猜它应该看起来像:
@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(...)
}
}
Run Code Online (Sandbox Code Playgroud)
此外,视图模型可以具有类似这样的功能(通过单击撰写屏幕中的“登录”按钮来触发
fun onSignIn() {
viewModelScope.launch {
// Make a suspending sign in network call
_uiState.value = InProgress
// Trigger navigation
_uiState.value = SignIn
}
}
Run Code Online (Sandbox Code Playgroud)
它rememberNavController有一个非常简单的源代码,您可以使用它在单例服务中创建它:
@Singleton
class NavigationService @Inject constructor(
@ApplicationContext context: Context,
) {
val navController = NavHostController(context).apply {
navigatorProvider.addNavigator(ComposeNavigator())
navigatorProvider.addNavigator(DialogNavigator())
}
}
Run Code Online (Sandbox Code Playgroud)
创建一个辅助视图模型以NavHostController与NavHost视图共享:
@HiltViewModel
class NavViewModel @Inject constructor(
navigationService: NavigationService,
): ViewModel() {
val controller = navigationService.navController
}
NavHost(
navController = hiltViewModel<NavViewModel>().controller,
startDestination = // ...
) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后在任何视图模型中,您可以注入它并用于导航:
@HiltViewModel
class ScreenViewModel @Inject constructor(
private val navigationService: NavigationService
): ViewModel() {
fun navigateToNextScreen() {
navigationService.navController.navigate(Destinations.NextScreen)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5281 次 |
| 最近记录: |