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

基于DataStore中AccessToken的存在性配置Jetpack Compose导航起始页面

基于DataStore中AccessToken的存在性配置Jetpack Compose导航起始页面

嘿,很高兴能帮到你!这个需求在Jetpack Compose导航场景里挺常见的,我来一步步给你拆解实现思路和具体代码~

核心思路

因为DataStore的读写是异步操作,我们没法在NavHost初始化时直接硬编码起始页面,得先监听AccessToken的状态变化,再根据状态动态决定导航的起始目的地,同时还要处理DataStore首次读取时的加载状态,避免用户看到不必要的页面跳转。

步骤1:封装DataStore的Token观测逻辑

首先你应该已经有一个管理DataStore的工具类了吧?如果还没有,可以参考下面的示例,封装一个能暴露Token状态Flow的类:

class TokenManager(private val dataStore: DataStore<Preferences>) {
    private val ACCESS_TOKEN_KEY = stringPreferencesKey("access_token")

    // 暴露一个Flow,实时返回当前Token的状态(null表示未登录)
    val accessTokenFlow: Flow<String?> = dataStore.data
        .map { preferences ->
            preferences[ACCESS_TOKEN_KEY]
        }

    // 保存Token的方法
    suspend fun saveAccessToken(token: String) {
        dataStore.edit { preferences ->
            preferences[ACCESS_TOKEN_KEY] = token
        }
    }

    // 删除Token的方法
    suspend fun deleteAccessToken() {
        dataStore.edit { preferences ->
            preferences.remove(ACCESS_TOKEN_KEY)
        }
    }
}

步骤2:在导航宿主中动态监听Token状态

在你的根导航Composable里,用collectAsStateWithLifecycle来收集Token的状态(这个API会自动跟随Composable生命周期管理收集,更安全),同时处理DataStore初始化加载的状态:

@Composable
fun AppNavigation(tokenManager: TokenManager) {
    val navController = rememberNavController()
    // 收集Token状态,初始值设为null
    val accessToken by tokenManager.accessTokenFlow.collectAsStateWithLifecycle(initialValue = null)
    // 标记DataStore是否完成首次读取
    val isDataStoreLoaded = remember { mutableStateOf(false) }

    // 等待DataStore首次读取完成
    LaunchedEffect(Unit) {
        tokenManager.accessTokenFlow.first()
        isDataStoreLoaded.value = true
    }

    // 加载完成前显示过渡界面,避免页面闪烁
    if (!isDataStoreLoaded.value) {
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            CircularProgressIndicator()
        }
    } else {
        NavHost(
            navController = navController,
            // 根据Token状态动态设置起始页
            startDestination = if (accessToken != null) "home_screen" else "login_screen"
        ) {
            composable("login_screen") {
                LoginScreen(navController = navController, tokenManager = tokenManager)
            }
            composable("home_screen") {
                HomeScreen(navController = navController, tokenManager = tokenManager)
            }
            // 其他页面路由...
        }
    }
}

步骤3:处理Token变化后的导航跳转

需要注意的是,NavHost的startDestination只在初始化时生效,当用户登录/注销导致Token变化时,我们需要手动触发导航并清理回退栈,避免用户按返回键回到之前的页面:

登录成功后跳转主页

在LoginScreen中保存Token后,执行导航:

@Composable
fun LoginScreen(navController: NavController, tokenManager: TokenManager) {
    // 假设这里是登录按钮的点击事件
    Button(onClick = {
        val newToken = "从后端获取的Token"
        LaunchedEffect(newToken) {
            tokenManager.saveAccessToken(newToken)
            // 跳转到主页并清空回退栈
            navController.navigate("home_screen") {
                popUpTo(navController.graph.startDestinationId) { inclusive = true }
            }
        }
    }) {
        Text("登录")
    }
}

注销后跳转登录页

在HomeScreen中执行注销操作后,跳转登录页:

@Composable
fun HomeScreen(navController: NavController, tokenManager: TokenManager) {
    Button(onClick = {
        LaunchedEffect(Unit) {
            tokenManager.deleteAccessToken()
            // 跳转到登录页并清空回退栈
            navController.navigate("login_screen") {
                popUpTo(navController.graph.startDestinationId) { inclusive = true }
            }
        }
    }) {
        Text("注销")
    }
}

一些最佳实践

  • 建议用Hilt将TokenManager作为单例注入,避免手动实例化带来的问题
  • 加载过渡界面可以做得更友好,比如加上应用Logo或提示文字
  • 不要在导航宿主外处理过多导航逻辑,尽量把导航事件封装在对应的页面Composable中

这样就能完美实现根据Token状态自动切换起始页面的需求啦~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 09:04:33