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

基于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启动时直接读取结果:

  1. 在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
        }
    }
}
  1. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:03:14