I have composable MainScreen and SettingsScreen. They all share a common ViewModel SettingsViewModel which is responsible for reading and saving theme settings.
In the MainActivity, before creating MainScreen, I get SettingsViewModel that internally receives data about which theme is used by app (via DataStore). Then I pass the theme as the app's theme parameter.
But the problem is that the default value (from ViewModel) is loaded first, and after delay - actual value from DataStore. What am I missing? Why is there such a huge delay (almost a second) and how to avoid it?
My MainActivity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val viewModel: SettingsViewModel = hiltViewModel()
val colorScheme = viewModel.colorScheme.collectAsState()// TODO loaded with default value first :(
Log.d("TAG", "MainActivity theme $colorScheme")
MyAppTheme(
darkTheme = when(colorScheme.value){
ColorScheme.AUTO -> isSystemInDarkTheme()
ColorScheme.LIGHT -> false
ColorScheme.DARK -> true
}
) {
// MainScreen
}
}
}
}
My SettingsViewModel
enum class ColorScheme{
AUTO, LIGHT, DARK
}
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val settingsRepository: SettingsRepository
): ViewModel() {
private val _colorScheme = MutableStateFlow(ColorScheme.AUTO)
val colorScheme: StateFlow<ColorScheme>
get() = _colorScheme
init {
Log.d("TAG", "SettingsViewModel init")
// Load settings
viewModelScope.launch {
_colorScheme.emit(ColorScheme.values()[settingsRepository.loadColorScheme()])
Log.d("TAG", "SettingsViewModel load & emit")
}
}
fun setColorScheme(colorScheme: ColorScheme){
_colorScheme.tryEmit(colorScheme)
viewModelScope.launch {
settingsRepository.saveColorScheme(colorScheme)
}
}
}
My Logcat
2023-08-13 11:34:22.315 SettingsViewModel init
2023-08-13 11:34:22.326 MainActivity theme = MutableState(value=AUTO)
2023-08-13 11:34:23.105 SettingsViewModel load & emit
2023-08-13 11:34:23.124 MainActivity theme = MutableState(value=LIGHT)
I already tried to remove default value from SettingsViewModel, but at the time of the first MainScreen recomposition, the theme variable has not yet been initialized. Maybe you can somehow postpone MainScreen recomposition until the app theme is known?
You should retrieve the Theme from your settings database outside of your activity, and store it in a global variable. And then load that global variable in your ViewModel's
initblock, instead of loading whatever default value you currently use.The easiest way to do this is by creating a persistent Application class, and anything you run in the Application's
onCreate()will be run immediately when the Application wakes up, before any of your activities are initialized.in your Manifest:
and your Application class:
and you can access the variable anywhere in your app by calling
App.colorScheme