You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 00:52:42