基于ROOM与COMPOSE的登录校验优化:避免启动闪屏问题
解决Compose启动时登录校验导致的闪屏问题
问题原因
你当前的实现中,LoginViewModel的_login初始值为false,启动时RootNavigationGraph会直接根据这个默认值把初始路由设为登录页。而登录状态校验是异步执行的,等校验完成更新状态后才会跳转到首页,这就导致了短暂显示登录页的闪屏现象。
解决方案
核心思路是在登录状态校验完成前,不加载任何业务页面,而是显示过渡占位UI;等状态明确后,再加载对应的导航图。具体步骤如下:
1. 扩展登录状态类型,区分校验中、已登录、未登录
用密封类定义三种状态,替代原来的布尔值,明确区分"校验未完成"的状态:
sealed class LoginStatus { object Loading : LoginStatus() object LoggedIn : LoginStatus() object NotLoggedIn : LoginStatus() }
2. 修改ViewModel,适配新状态逻辑
把原来的MutableStateFlow<Boolean>替换为MutableStateFlow<LoginStatus>,初始化时设为Loading,校验完成后更新对应状态:
@HiltViewModel class LoginViewModel @Inject constructor( private val loginRepository: LoginRepository ) : ViewModel() { private val _loginStatus = MutableStateFlow<LoginStatus>(LoginStatus.Loading) val loginStatus: StateFlow<LoginStatus> = _loginStatus init { viewModelScope.launch { checkLogin() } } suspend fun checkLogin() { try { val res = loginRepository.getLastLogin() _loginStatus.value = if (res.checked == true) LoginStatus.LoggedIn else LoginStatus.NotLoggedIn } catch (e: Exception) { _loginStatus.value = LoginStatus.NotLoggedIn } } suspend fun logout() { val res = loginRepository.getLastLogin() res.checked = false loginRepository.setChecked(res) _loginStatus.value = LoginStatus.NotLoggedIn } }
3. 更新导航图,根据状态显示对应内容
在RootNavigationGraph中,根据loginStatus的状态分支处理:
- 状态为
Loading时,显示过渡UI(比如应用Logo、空白页或加载提示) - 状态明确后,加载对应的首页或登录页导航图
@Composable fun RootNavigationGraph(navController: NavHostController) { val viewModel: LoginViewModel = hiltViewModel() val loginStatus by viewModel.loginStatus.collectAsState() when (loginStatus) { LoginStatus.Loading -> { // 替换为你的启动过渡UI,比如居中显示应用Logo Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text(text = "启动中...") } } LoginStatus.LoggedIn -> { NavHost( navController = navController, route = AppGraph.initial.ROOT, startDestination = AppGraph.home.ROOT ) { homeNavGraph(navController = navController) authNavGraph(navController = navController) } } LoginStatus.NotLoggedIn -> { NavHost( navController = navController, route = AppGraph.initial.ROOT, startDestination = AppGraph.auth.ROOT ) { homeNavGraph(navController = navController) authNavGraph(navController = navController) } } } }
额外优化思路
如果不想显示过渡加载UI,也可以在Application初始化阶段提前完成登录状态校验,把结果存在内存中,ViewModel启动时直接读取结果:
- 在Application类中异步校验登录状态:
class MyApp : Application() { companion object { var cachedLoginStatus: Boolean? = null } override fun onCreate() { super.onCreate() CoroutineScope(Dispatchers.IO).launch { // 通过依赖注入获取LoginRepository实例 val loginRepository = // 你的获取逻辑 val res = loginRepository.getLastLogin() cachedLoginStatus = res.checked == true } } }
- ViewModel中等待校验结果:
init { viewModelScope.launch { // 等待Application中的校验完成 while (MyApp.cachedLoginStatus == null) { delay(50) } _loginStatus.value = if (MyApp.cachedLoginStatus == true) LoginStatus.LoggedIn else LoginStatus.NotLoggedIn } }
这种方式能让ViewModel更快拿到状态,但会增加Application与业务逻辑的耦合性,按需选择即可。
内容的提问来源于stack exchange,提问作者Mateus Trajano
相关产品推荐
相关产品推荐

