Jetpack Compose使用viewModel进行导航

5

我正在尝试在viewModel中使用Jetpack Compose导航。当触发导航时,没有任何反应。这是我的方法:

我定义了NavigationDestination。在我的情况下,我有四个屏幕:欢迎、登录、注册和调查。

interface NavigationDestination {
    val route: String
}

sealed class Screen(override val route: String) : NavigationDestination {

    object WelcomeScreen : Screen("welcome")
    object SignInScreen : Screen("signIn")
    object SignUpScreen : Screen("signUp")
    object SurveyScreen : Screen("survey")

}

Navigator是一个与默认屏幕WelcomeScreen一起使用的工具,它可以展示当前的目标位置。

class Navigator {

    var destination: MutableStateFlow<NavigationDestination> = MutableStateFlow(Screen.WelcomeScreen)

    fun navigate(destination: NavigationDestination) {
        this.destination.value = destination
    }

}

在主组合中,我获取 NavHostController 并监听 Navigator 的变化。

@Composable
fun JetSurvey0App() {
    val welcomeViewModel: WelcomeViewModel = viewModel(factory = WelcomeViewModelFactory())
    val navigator = Navigator()
    val navController = rememberNavController()
    val destination by navigator.destination.collectAsState()

    LaunchedEffect(destination) {
        if (navController.currentDestination?.route != destination.route) {
            navController.navigate(destination.route)
        }
    }

    NavHost(navController = navController, startDestination = navigator.destination.value.route) {
        composable(Screen.WelcomeScreen.route) {
            WelcomeScreen(
                onEvent = { event ->
                    when (event) {
                        is WelcomeEvent.SignInSignUp -> welcomeViewModel.handleContinue(event.email)
                        WelcomeEvent.SignInAsGuest -> welcomeViewModel.signInAsGuest()
                    }
                }
            )
        }
        composable(Screen.SignUpScreen.route) {
            SignUp()
        }
        composable(Screen.SignInScreen.route) {
            SignIn()
        }
        composable(Screen.SurveyScreen.route) {
            SurveyQuestionsScreen()
        }


    }
}

WelcomeViewModel中,我通过调用Navigator来执行解耦导航,如下所示。

class WelcomeViewModel(
    private val userRepository: UserRepository,
    private val navigator: Navigator
    ) : ViewModel() {

    fun handleContinue(email: String) {
        if (userRepository.isKnownUserEmail(email)) {
            viewModelScope.launch {
              navigator.navigate(Screen.SignInScreen)
            }
        } else {
          viewModelScope.launch {
              navigator.navigate(Screen.SignUpScreen)
          }
        }
    }

    fun signInAsGuest() {
        viewModelScope.launch {
            navigator.navigate(Screen.SurveyScreen)
        }
        userRepository.signInAsGuest()
    }


}

class WelcomeViewModelFactory : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(WelcomeViewModel::class.java)) {
            return WelcomeViewModel(UserRepository, Navigator()) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
1个回答

3

collectAsState 会在每次从流中发送新值时触发重新组合(recomposition)。这意味着 JetSurvey0App 将被重新调用。

您正在尝试使用 navigator.destination 进行导航,但是每次重新组合时都会创建一个新对象:

val navigator = Navigator()
val destination by navigator.destination.collectAsState()

你可以将WelcomeViewModel.navigator的访问权限从private改为public,并收集其destination - 随着该对象状态的变化而变化。

了解有关重新组合的更多信息,请参阅Compose Mental Model


为了做这件事,我需要三个勾选标记。你能给我一个吗?然后我会完成它。 - the great warrior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接