In my Android project, I'm using DataStore and StateFlow to store and change the UI state. The problem is, every time the app starts, because of the necessary initial value of StateFlow, the corresponding theme setting will always set to the initial value before set to the value read from DataStore, and this will lead to a visual hit(change from Light to Dark or Dark to light) for users.
The following text shows the detail on achieving that(I use this sample code as reference):
- Get the value from DataStore as a Flow and assign it to
observeSelectedDarkMode(): Flow<String>
function in myAppRepository
class.
class AppRepository(
val context: Context
) : AppRepository {
private val dataStore = context.dataStore
override fun observeSelectedDarkMode(): Flow<String> = dataStore.data.map { it[KEY_SELECTED_DARK_MODE] ?: DEFAULT_VALUE_SELECTED_DARK_MODE }
...
}
- Convert it to StateFlow using
.stateIn()
in mySettingsViewModel
class.
class SettingsViewModel(
private val appRepository: AppRepository
) : ViewModel() {
val selectedDarkModel = appRepository.observeSelectedDarkMode().stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
AppDataStore.DEFAULT_VALUE_SELECTED_DARK_MODE
)
...
}
- Use
.collectAsState()
in myCampusHelperTheme()
fun to observe the change.
@Composable
fun CampusHelperTheme(
appContainer: AppContainer,
content: @Composable () -> Unit
) {
val settingsViewModel: SettingsViewModel = viewModel(
factory = SettingsViewModel.provideFactory(appContainer.appRepository)
)
val selectedDarkMode by settingsViewModel.selectedDarkModel.collectAsState()
val darkTheme = when (selectedDarkMode) {
"On" -> true
"Off" -> false
else -> isSystemInDarkTheme()
}
val context = LocalContext.current
val colorScheme = if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
...
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
It seems that everything is good, but when the selectedDarkMode
value is different from its default value AppDataStore.DEFAULT_VALUE_SELECTED_DARK_MODE
(Follow System), the app will turn from Dark to Light or from Light to Dark, which would give users a bad experience.
So what I want is let the app skip the initial value of StateFlow and then the app can set to what the user choose once it launch, then there will not have a visual hit to users.
I search on this site, and found this ask: How to use DataStore with StateFlow and Jetpack Compose?, but it has no content about the initial value I am looking for, I also found this ask: Possible to ignore the initial value for a ReactiveObject?, but it is in C# and ReactiveUI, which is not suitable for me.
How should I achieve this?