基于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
相关产品推荐
相关产品推荐

