在 Jetpack Compose 中将深色主题保存到数据存储后,屏幕在启动时闪烁
Screen flashes on launch after saving dark theme to dataStore in jetpack compose
我有一个简单的 Light/Dark 主题切换,我正在将主题保存到数据存储中。
保存主题并关闭应用程序后,屏幕会暂时以浅色模式启动,然后切换到深色主题。我错过了什么吗?这是我在这里处理的副作用吗?请指教
这是解释案例的 GIF
https://i.stack.imgur.com/7YH7z.gif
这是 theme.kt 文件的代码
private val DarkColorPalette = darkColors(
primary = White100,
)
private val LightColorPalette = lightColors(
primary = Blue100,
)
@Composable
fun MyTheme(
darkTheme: MutableState<Boolean>,
content: @Composable () -> Unit) {
val colors = if (darkTheme.value) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
这是 MainActivity.kt 文件的代码
// DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
// DataStore key
val USER_THEME = booleanPreferencesKey("user_theme")
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Reading theme
val themeFlow: Flow<Boolean> = dataStore.data
.map { preferences ->
preferences[USER_THEME] ?: false
}
// Toggle and save theme
suspend fun toggleTheme() {
dataStore.edit { settings ->
val currentTheme = settings[USER_THEME] ?: false
settings[USER_THEME] = !currentTheme
}
}
super.onCreate(savedInstanceState)
setContent {
// Getting the theme from DataStore
// If the initial value is true, it works fine on dark mode but not on Light
// If the initial value is false, it works fine on light mode but not on dark
val userTheme = themeFlow.collectAsState(initial = false)
// Mutable state of the theme to be passed into "My Theme"
val themeState: MutableState<Boolean> = mutableStateOf(userTheme.value)
// Scope to toggle theme on click
val scope = rememberCoroutineScope()
// UI
MyTheme (darkTheme = themeState) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Image(
painter = painterResource(id = R.drawable.icon_dark),
contentDescription = "Theme Switcher",
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
modifier = Modifier
.clickable {
scope.launch {
toggleTheme()
}
}
)
}
}
}
}
}
在 Francesc 回答后编辑代码:
- 删除了“userTheme”行并向该行添加了 runBlocking
val themeState: MutableState<Boolean> = runBlocking { mutableStateOf(themeFlow.first())}
- 通过按钮手动切换主题值:
scope.launch {
themeState.value = !themeState.value
saveTheme() // renamed it to saveTheme instead of toggleTheme
}
您的主题是从商店异步读取的,最初它默认为 false
(不是深色主题),因此它以浅色模式显示。稍后当流发出并且模式为暗模式时,UI 切换到暗模式。
由于数据存储的异步性,您有 2 个选择:
- 同步读取主题初始值,使用
runBlocking
- 在等待读取时显示一些占位符,可能是
Box
带有您的应用程序徽标(就像启动画面),或者什么都不显示,空白 canvas
我有一个简单的 Light/Dark 主题切换,我正在将主题保存到数据存储中。 保存主题并关闭应用程序后,屏幕会暂时以浅色模式启动,然后切换到深色主题。我错过了什么吗?这是我在这里处理的副作用吗?请指教
这是解释案例的 GIF https://i.stack.imgur.com/7YH7z.gif
这是 theme.kt 文件的代码
private val DarkColorPalette = darkColors(
primary = White100,
)
private val LightColorPalette = lightColors(
primary = Blue100,
)
@Composable
fun MyTheme(
darkTheme: MutableState<Boolean>,
content: @Composable () -> Unit) {
val colors = if (darkTheme.value) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
这是 MainActivity.kt 文件的代码
// DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
// DataStore key
val USER_THEME = booleanPreferencesKey("user_theme")
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Reading theme
val themeFlow: Flow<Boolean> = dataStore.data
.map { preferences ->
preferences[USER_THEME] ?: false
}
// Toggle and save theme
suspend fun toggleTheme() {
dataStore.edit { settings ->
val currentTheme = settings[USER_THEME] ?: false
settings[USER_THEME] = !currentTheme
}
}
super.onCreate(savedInstanceState)
setContent {
// Getting the theme from DataStore
// If the initial value is true, it works fine on dark mode but not on Light
// If the initial value is false, it works fine on light mode but not on dark
val userTheme = themeFlow.collectAsState(initial = false)
// Mutable state of the theme to be passed into "My Theme"
val themeState: MutableState<Boolean> = mutableStateOf(userTheme.value)
// Scope to toggle theme on click
val scope = rememberCoroutineScope()
// UI
MyTheme (darkTheme = themeState) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Image(
painter = painterResource(id = R.drawable.icon_dark),
contentDescription = "Theme Switcher",
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
modifier = Modifier
.clickable {
scope.launch {
toggleTheme()
}
}
)
}
}
}
}
}
在 Francesc 回答后编辑代码:
- 删除了“userTheme”行并向该行添加了 runBlocking
val themeState: MutableState<Boolean> = runBlocking { mutableStateOf(themeFlow.first())}
- 通过按钮手动切换主题值:
scope.launch {
themeState.value = !themeState.value
saveTheme() // renamed it to saveTheme instead of toggleTheme
}
您的主题是从商店异步读取的,最初它默认为 false
(不是深色主题),因此它以浅色模式显示。稍后当流发出并且模式为暗模式时,UI 切换到暗模式。
由于数据存储的异步性,您有 2 个选择:
- 同步读取主题初始值,使用
runBlocking
- 在等待读取时显示一些占位符,可能是
Box
带有您的应用程序徽标(就像启动画面),或者什么都不显示,空白 canvas