Jetpack Compose条件导航问题:非首次启动仍显示引导页
问题原因
你的问题根源在于OnBoardingViewModel中_onBoardingState的初始值为false,而从DataStore读取真实状态是异步操作。Compose会先使用这个初始值创建NavHost,将起始页设为引导页;等DataStore返回非首次启动的真实值(true)后,虽然startDest会更新,但NavHost不会重新初始化,导致短暂显示引导页后才跳转至登录页。
解决方法
方法1:等待状态加载完成后渲染NavHost
通过给ViewModel的状态添加加载状态标记,先显示过渡页面(如加载动画或启动页),等DataStore的真实状态返回后,再初始化正确的NavHost。
步骤1:修改ViewModel,添加加载状态
@HiltViewModel class OnBoardingViewModel @Inject constructor( private val dataStore: PreferencesDatastore ) : ViewModel() { private val _onBoardingState = MutableStateFlow<UiState>(UiState.Loading) val onBoardingState = _onBoardingState.asStateFlow() init { viewModelScope.launch(Dispatchers.IO) { val isFirstTime = dataStore.getOnBoardingState().first() _onBoardingState.value = UiState.Success(isFirstTime) } } fun setFirstTimeInstalled() { viewModelScope.launch(Dispatchers.IO) { dataStore.saveOnBoardingState(true) } } sealed class UiState { object Loading : UiState() data class Success(val isFirstTimeInstalled: Boolean) : UiState() } }
步骤2:在Navigation组件中处理状态
@Composable fun Navigation() { val navController = rememberNavController() val onBoardingViewModel = hiltViewModel<OnBoardingViewModel>() val uiState by onBoardingViewModel.onBoardingState.collectAsStateWithLifecycle() when (uiState) { OnBoardingViewModel.UiState.Loading -> { // 显示过渡页面,避免空白或闪屏 Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } is OnBoardingViewModel.UiState.Success -> { val isFirstTime = (uiState as OnBoardingViewModel.UiState.Success).isFirstTimeInstalled val startDest = getStartDest(isFirstTime) NavHost( navController = navController, startDestination = startDest, enterTransition = { slideIn() }, exitTransition = { slideOut() }, ) { composable("onBoarding") { OnBoardingScreen(onComplete = { onBoardingViewModel.setFirstTimeInstalled() navController.navigate("login") { popUpTo("onBoarding") { inclusive = true } } }) } composable("login") { LoginScreen() } } } } }
方法2:使用过渡路由跳转
如果不想显示加载动画,可以设置一个过渡路由(如splash),在该页面中监听状态变化后直接跳转到目标页面,避免显示引导页。
@Composable fun Navigation() { val navController = rememberNavController() val onBoardingViewModel = hiltViewModel<OnBoardingViewModel>() val isFirstTime by onBoardingViewModel.onBoardingState.collectAsStateWithLifecycle() NavHost( navController = navController, startDestination = "splash", enterTransition = { slideIn() }, exitTransition = { slideOut() }, ) { composable("splash") { LaunchedEffect(isFirstTime) { val destination = getStartDest(isFirstTime) navController.navigate(destination) { popUpTo("splash") { inclusive = true } } } // 这里可以显示应用Logo或空白页 Box(modifier = Modifier.fillMaxSize()) } composable("onBoarding") { OnBoardingScreen(onComplete = { onBoardingViewModel.setFirstTimeInstalled() navController.navigate("login") { popUpTo("onBoarding") { inclusive = true } } }) } composable("login") { LoginScreen() } } }
内容的提问来源于stack exchange,提问作者Francisco Godoy
相关产品推荐
相关产品推荐

