Jetpack Compose启动页实现问题:数据加载后跳转异常
如何在Jetpack Compose中正确实现启动页?
当前实现存在问题:应用启动后会先跳转到OnBoardingScreen,等ViewModel加载完数据后才重新跳转到HomeScreen。期望实现等待ViewModel数据加载完成后,再决定启动目标页面,该如何调整?
当前代码实现
NavHost( navController = navController, startDestination = determineStartDestination(viewModel), modifier = Modifier.fillMaxSize() ) { // 页面路由注册 } private fun determineStartDestination(userModel: HomeViewModel): String { return if (userModel.homeState.value.emailFrom.isNotEmpty() && userModel.homeState.value.emailTo.isNotEmpty()) { OnHomeNav.getRoute() } else { OnBoardingScreenNav.getRoute() } }
尝试过的无效方案
修改AndroidManifest设置启动主题:
android:theme="@style/Theme.MyApp.Starting"
在MainActivity中配置SplashScreen:
installSplashScreen().apply { setKeepOnScreenCondition { homeViewModel.homeState.value.emailFrom.isNotEmpty() && homeViewModel.homeState.value.emailTo.isNotEmpty() } } setContent { [...] }
启动页能正常显示,但setKeepOnScreenCondition逻辑未生效,仍会出现:启动页→OnBoardingScreen(显示1秒)→HomeScreen的流程。
临时但不可接受的解决方案
通过delay()等待状态更新,本质是依赖不可靠的时间延迟:
init { viewModelScope.launch { getUser() } } // 修改前的getUser方法 private fun getUser() { viewModelScope.launch { userRepository.getUser().collectLatest { userModel -> homeState.update { copy( userModel = userModel, nameAndSurname = userModel?.nameAndSurname ?: "", number = userModel?.phoneNumber ?: "", email = userModel?.email ?: "", registrationNumber = userModel?.registrationNumber ?: "", emailTo = userModel?.emailTo ?: "", emailFrom = userModel?.emailFrom ?: "" ) } } } } // 修改后的getUser方法 private fun getUser() { viewModelScope.launch { userRepository.getUser().filterNotNull().collectLatest { userModel -> homeState.update { copy( userModel = userModel, nameAndSurname = userModel?.nameAndSurname ?: "", number = userModel?.phoneNumber ?: "", email = userModel?.email ?: "", registrationNumber = userModel?.registrationNumber ?: "", emailTo = userModel?.emailTo ?: "", emailFrom = userModel?.emailFrom ?: "" ) } delay(100L) checkIfUserIsLoggedIn() } } }
问题根源:错误依赖时间延迟来等待状态更新,而非通过可靠的状态标记判断加载完成。
正确实现方案
核心思路:通过ViewModel维护加载状态,配合系统SplashScreen API,确保启动页显示到数据加载完成,再根据最终状态决定跳转目标。
1. 修正ViewModel逻辑,添加加载状态标记
在ViewModel中新增isLoading状态,初始为true,数据加载完成后切换为false,确保状态更新的可靠性:
class HomeViewModel(private val userRepository: UserRepository) : ViewModel() { private val _homeState = MutableStateFlow(HomeState()) val homeState = _homeState.asStateFlow() // 新增加载状态标记 private val _isLoading = MutableStateFlow(true) val isLoading = _isLoading.asStateFlow() init { getUser() } private fun getUser() { viewModelScope.launch { try { userRepository.getUser() .filterNotNull() .collectLatest { userModel -> _homeState.update { copy( userModel = userModel, nameAndSurname = userModel.nameAndSurname ?: "", number = userModel.phoneNumber ?: "", email = userModel.email ?: "", registrationNumber = userModel.registrationNumber ?: "", emailTo = userModel.emailTo ?: "", emailFrom = userModel.emailFrom ?: "" ) } } } finally { // 无论是否获取到用户数据,加载完成后都标记为非加载状态 _isLoading.value = false } } } } data class HomeState( val userModel: UserModel? = null, val nameAndSurname: String = "", val number: String = "", val email: String = "", val registrationNumber: String = "", val emailTo: String = "", val emailFrom: String = "" )
2. 正确配置SplashScreen,基于加载状态控制显示
在MainActivity中,用isLoading控制启动页的显示时长,确保数据加载完成后再进入目标页面:
class MainActivity : ComponentActivity() { private val homeViewModel by viewModels<HomeViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen().apply { setKeepOnScreenCondition { // 只要加载未完成,就保持启动页显示 homeViewModel.isLoading.value } } setContent { MyAppTheme { val navController = rememberNavController() // 收集加载完成后的最终状态 val homeState by homeViewModel.homeState.collectAsState() NavHost( navController = navController, // 此时数据已加载完成,可正确判断启动目标 startDestination = determineStartDestination(homeState), modifier = Modifier.fillMaxSize() ) { composable(OnBoardingScreenNav.getRoute()) { OnBoardingScreen() } composable(OnHomeNav.getRoute()) { HomeScreen() } } } } } private fun determineStartDestination(homeState: HomeState): String { return if (homeState.emailFrom.isNotEmpty() && homeState.emailTo.isNotEmpty()) { OnHomeNav.getRoute() } else { OnBoardingScreenNav.getRoute() } } }
方案说明
- 之前的SplashScreen方案失效原因:初始状态下
emailFrom和emailTo为空,导致setKeepOnScreenCondition直接返回false,启动页提前消失,此时数据还在加载中。 - 新方案通过
isLoading状态精准控制启动页显示时长,确保数据加载完成后,NavHost才能获取到最新的homeState,从而正确判断启动目标页面。 - 无需依赖
delay(),状态更新是同步可靠的,避免了时间延迟带来的不确定性。
内容的提问来源于stack exchange,提问作者user21300258
相关产品推荐
相关产品推荐

