Jetpack 使用 viewModel 组合导航
Jetpack compose navigation with viewModel
我正在尝试在 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")
}
}
每次向流发出新值时,collectAsState
都会触发重组。这意味着 JetSurvey0App
将是 re-called.
您正在尝试使用 navigator.destination
进行导航,但您在每次重组时都创建了一个新对象:
val navigator = Navigator()
val destination by navigator.destination.collectAsState()
您可以制作 WelcomeViewModel.navigator
public
而不是 private
并收集其 destination
- 当您更改此特定对象的状态时。
阅读 Compose Mental Model 中有关重组的更多信息。
我正在尝试在 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")
}
}
collectAsState
都会触发重组。这意味着 JetSurvey0App
将是 re-called.
您正在尝试使用 navigator.destination
进行导航,但您在每次重组时都创建了一个新对象:
val navigator = Navigator()
val destination by navigator.destination.collectAsState()
您可以制作 WelcomeViewModel.navigator
public
而不是 private
并收集其 destination
- 当您更改此特定对象的状态时。
阅读 Compose Mental Model 中有关重组的更多信息。