Jetpack Compose Deeplink条件导航及登录校验问题求解决方案
Jetpack Compose 带登录校验的Deeplink条件导航解决方案
核心问题拆解
你的问题本质是Deeplink触发时绕过了登录校验逻辑,原因是系统通过onNewIntent/onResume直接传递导航指令,而没有经过全局的登录状态校验环节。下面给出针对性的落地方案,无需依赖复杂的多NavHost架构,用单NavHost配合全局状态监听即可解决。
解决方案实现
1. 统一导航入口,拦截所有Deeplink请求
不要让NavHost直接处理Deeplink,而是将所有导航请求(包括启动器启动、Deeplink跳转)统一路由到登录校验逻辑后再执行导航。
2. 用全局ViewModel监听登录状态
利用你已有的Application-ViewModel维护登录状态,在Activity中监听状态变化,联动导航逻辑。
3. 代码示例
全局登录状态ViewModel
class AppViewModel(application: Application) : AndroidViewModel(application) { private val _isLoggedIn = MutableStateFlow(false) val isLoggedIn = _isLoggedIn.asStateFlow() // 校验本地登录状态(比如从SharedPreferences读取) fun checkAuthStatus() { val prefs = application.getSharedPreferences("auth", Context.MODE_PRIVATE) _isLoggedIn.value = prefs.getBoolean("is_logged_in", false) } // 登录成功后更新状态 fun markUserLoggedIn() { val prefs = application.getSharedPreferences("auth", Context.MODE_PRIVATE) prefs.edit().putBoolean("is_logged_in", true).apply() _isLoggedIn.value = true } }
MainActivity导航逻辑处理
class MainActivity : ComponentActivity() { private lateinit var appViewModel: AppViewModel private var pendingDeepLink: Uri? = null // 保存待处理的Deeplink override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) appViewModel = ViewModelProvider(this)[AppViewModel::class.java] appViewModel.checkAuthStatus() handleIncomingIntent(intent) // 处理启动时的Intent setContent { MyAppTheme { val isLoggedIn by appViewModel.isLoggedIn.collectAsStateWithLifecycle() val navController = rememberNavController() // 监听登录状态+待处理Deeplink,触发导航 LaunchedEffect(isLoggedIn, pendingDeepLink) { when { isLoggedIn -> { pendingDeepLink?.let { uri -> // 解析Deeplink为对应路由 val targetRoute = parseDeepLink(uri) navController.navigate(targetRoute) { popUpTo(navController.graph.startDestinationId) { inclusive = true } } pendingDeepLink = null } ?: navController.navigate(Screen.Home.route) { popUpTo(navController.graph.startDestinationId) { inclusive = true } } } else -> { // 未登录时强制跳转到登录页 navController.navigate(Screen.Login.route) { popUpTo(navController.graph.startDestinationId) { inclusive = true } } } } } NavHost(navController = navController, startDestination = Screen.Splash.route) { composable(Screen.Splash.route) { // 闪屏页仅作为占位,实际导航由LaunchedEffect控制 SplashScreen() } composable(Screen.Login.route) { LoginScreen(onLoginSuccess = { appViewModel.markUserLoggedIn() }) } composable(Screen.Home.route) { HomeScreen() } composable(Screen.PremiumContent.route) { PremiumContentScreen() } // 其他需要登录的页面路由 } } } } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) intent?.let { handleIncomingIntent(it) } } // 处理所有传入的Intent,提取并保存Deeplink private fun handleIncomingIntent(intent: Intent) { if (intent.action == Intent.ACTION_VIEW && intent.data != null) { pendingDeepLink = intent.data appViewModel.checkAuthStatus() // 重新校验登录状态(防止用户已登出) } } // 根据你的Deeplink规则解析为路由 private fun parseDeepLink(uri: Uri): String { return when (uri.path) { "/premium" -> Screen.PremiumContent.route else -> Screen.Home.route } } } // 路由定义 sealed class Screen(val route: String) { object Splash : Screen("splash") object Login : Screen("login") object Home : Screen("home") object PremiumContent : Screen("premium_content") }
关键细节说明
- pendingDeepLink缓存:将Deeplink请求暂存,等待登录状态校验完成后再执行导航,避免直接跳转绕过校验。
- LaunchedEffect联动:通过监听登录状态和待处理Deeplink的变化,确保只有登录状态满足时才跳转到目标页面。
- onNewIntent处理:覆盖该方法捕获后续的Deeplink请求,重复校验流程。
关于多NavHost的适用场景
多NavHost适合登录前后导航栈完全隔离的场景(比如登录后不需要回到登录页,且两个栈的路由完全独立)。如果只是需要前置登录校验,单NavHost配合状态监听足够简洁,无需引入额外复杂度。
内容的提问来源于stack exchange,提问作者Kitesurfer
相关产品推荐
相关产品推荐

