Compose Navigation中ViewModel字段变更时如何触发页面跳转
问题解决方案
一、现有State方案的监听实现
你在ViewModel中定义的screen是Compose原生的State类型,在Composable中可以直接读取订阅其变化,由于页面跳转属于一次性副作用操作,不能直接放在重组逻辑中执行,需要搭配LaunchedEffect处理,同时要注意跳转后重置状态,避免用户回退到当前页面时重复触发跳转:
@Composable fun CreateAccountScreen( navController: NavController, viewModel: CreateAccountViewModel = hiltViewModel() ) { val targetRoute = viewModel.screen.value // 仅当targetRoute变化时触发执行 LaunchedEffect(targetRoute) { if (targetRoute.isNotEmpty()) { navController.navigate(targetRoute) // 跳转后重置状态,需先在Screen枚举中新增route为空的Empty项 viewModel.setScreen(Screen.Empty) } } // 原有页面UI逻辑 // ... }
二、更优的导航实现方案
方案优势
用State存储导航事件需要手动处理状态重置,很容易出现状态残留导致的重复跳转bug,更推荐用单次消费的事件流实现导航通知,事件消费后自动失效,不需要手动重置状态,是Compose官方推荐的导航场景实现方案。
实现代码
- 修改ViewModel逻辑,用Channel发送导航事件:
@HiltViewModel class CreateAccountViewModel @Inject constructor( private val cs: CS ) : ViewModel() { // 定义缓存容量为1的事件通道,事件仅可被消费一次 private val _navigationEvent = Channel<Screen>(Channel.BUFFERED) val navigationEvent = _navigationEvent.receiveAsFlow() private val signUpCallback = object : SignUpHandler { override fun onSuccess(user: User?, signUpResult: SignUpResult?) { // viewModelScope发送导航事件 viewModelScope.launch { _navigationEvent.send(Screen.VerifyAccountScreen) } Log.i(Constants.TAG, "sign up success") } override fun onFailure(exception: Exception?) { Log.i(Constants.TAG, "sign up failure ") } } }
- Composable中收集事件执行跳转:
@Composable fun CreateAccountScreen( navController: NavController, viewModel: CreateAccountViewModel = hiltViewModel() ) { // 页面创建后仅初始化一次事件收集 LaunchedEffect(Unit) { viewModel.navigationEvent.collect { targetScreen -> navController.navigate(targetScreen.route) } } // 原有页面UI逻辑 // ... }
内容的提问来源于stack exchange,提问作者Beesz
相关产品推荐
相关产品推荐

