Jetpack Compose自定义主题模式下启动屏适配问题
自定义主题适配启动屏的解决方案
问题核心
你已经实现了自定义主题切换,但切换主题后首次启动应用时,启动屏无法正确应用新的主题设置,第二次启动才显示正常。这是因为启动屏主题的设置时机和偏好值读取的异步性冲突导致的。
根本原因
当前代码中,SplashViewModel通过异步协程读取偏好设置的主题值,但installSplashScreen()的执行早于偏好值加载完成,导致首次启动时使用了_isDarkMode的初始默认值(true),等偏好值更新时,启动屏已经无法动态修改主题。
解决方案
直接在Activity的onCreate方法中同步读取偏好主题值,确保在初始化启动屏之前就拿到正确的主题配置,再设置启动屏主题。
修改代码
- 在Activity中注入
UserRepository,同步读取主题值:
@Inject lateinit var splashViewModel: SplashViewModel @Inject lateinit var repository: UserRepository // 添加该注入 @RequiresApi(Build.VERSION_CODES.S) override fun onCreate(savedInstanceState: Bundle?) { // 同步读取偏好中的主题设置,确保启动屏初始化前拿到正确值 val isDarkMode = runBlocking { repository.readDarkModePreference().first() } // 初始化启动屏并立即设置对应主题 val splashScreen = installSplashScreen() if (isDarkMode) { splashScreen.setSplashScreenTheme(R.style.Theme_App_StartingDarkMode) } else { splashScreen.setSplashScreenTheme(R.style.Theme_App_StartingLightMode) } // 保持启动屏显示直到加载完成 splashScreen.setKeepOnScreenCondition { splashViewModel.isLoading.value } super.onCreate(savedInstanceState) // 以下setContent代码保持不变 setContent { val userViewModel: UserViewModel = viewModel() val isDarkTheme = userViewModel.getSavedTheme().collectAsState(initial = true).value userViewModel.observeLifecycleEvents(LocalLifecycleOwner.current.lifecycle) SharpWalletTheme(darkTheme = isDarkTheme) { val startDestination by splashViewModel.startDestination val navController = rememberNavController() val isUserAuth by userViewModel.isUserAuth Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { RootNavigationGraph( navHostController = navController, startDestination = startDestination, userViewModel = userViewModel, toAuthUser = isUserAuth, ) } } } }
- 确保启动屏主题的Style配置正确(对应深色/浅色):
<!-- 浅色启动屏主题 --> <style name="Theme.App.StartingLightMode" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground">@color/light_splash_bg</item> <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_light</item> <item name="postSplashScreenTheme">@style/SharpWalletTheme.Light</item> </style> <!-- 深色启动屏主题 --> <style name="Theme.App.StartingDarkMode" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground">@color/dark_splash_bg</item> <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_dark</item> <item name="postSplashScreenTheme">@style/SharpWalletTheme.Dark</item> </style>
关键说明
- 使用
runBlocking同步读取Flow的第一个值是安全的,因为偏好设置必然存在默认值或用户已设置的值,不会长时间阻塞主线程。 - 启动屏主题必须在
installSplashScreen()之后立即设置,避免启动屏绘制后无法修改主题样式。 postSplashScreenTheme要与应用的对应主题匹配,保证启动屏到应用界面的过渡流畅无闪烁。
内容的提问来源于stack exchange,提问作者Ravikant sahu
相关产品推荐
相关产品推荐

