I try to give the user the choice whether to use the UI Mode 'light', 'dark' or the 'system' settings. I would like to save the selection as DataStore.
The drop down menu for the user selection is not loading the value from DataStore. It is always showing the initialvalue from stateIn() when loading the screen.
SettingsManager:
val Context.dataStoreUiSettings: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_UI_NAME)
object PreferencesKeys {
val UI_MODE: Preferences.Key<Int> = intPreferencesKey("ui_mode")
}
class SettingsManager @Inject constructor(private val context: Context) { //private val dataStore: DataStore<Preferences>
private val TAG: String = "UserPreferencesRepo"
// Configuration.UI_MODE_NIGHT_UNDEFINED, Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_NO
suspend fun setUiMode(uiMode: Int) {
context.dataStoreUiSettings.edit { preferences ->
preferences[UI_MODE] = uiMode
}
}
fun getUiMode(key: Preferences.Key<Int>, default: Int): Flow<Int> {
return context.dataStoreUiSettings.data
.catch { exception ->
if (exception is IOException) {
Timber.i("Error reading preferences: $exception")
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preference ->
preference[key] ?: default
}
}
fun <T> getDataStore(key: Preferences.Key<T>, default: Any): Flow<Any> {
return context.dataStoreUiSettings.data
.catch { exception ->
if (exception is IOException) {
Timber.i("Error reading preferences: $exception")
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preference ->
preference[key] ?: default
}
}
suspend fun clearDataStore() {
context.dataStoreUiSettings.edit { preferences ->
preferences.clear()
}
}
suspend fun removeKeyFromDataStore(key: Preferences.Key<Any>) {
context.dataStoreUiSettings.edit { preference ->
preference.remove(key)
}
}
}
ViewModel:
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val settingsUseCases: SettingsUseCases,
private val settingsManager: SettingsManager,
) : ViewModel() {
private val _selectableUiModes = mapOf(
UI_MODE_NIGHT_UNDEFINED to "System",
UI_MODE_NIGHT_NO to "Light",
UI_MODE_NIGHT_YES to "Dark"
)
val selectableUiModes = _selectableUiModes
val currentUiMode: StateFlow<Int?> = settingsManager.getUiMode(UI_MODE, UI_MODE_NIGHT_UNDEFINED).stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = null,
)
init {
Timber.i("SettingsViewModel created")
}
fun setUiMode(uiModeKey: Int) {
viewModelScope.launch(Dispatchers.IO) {
settingsManager.setUiMode(uiModeKey)
}
}
}
Compose:
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun OutlinedDropDown(
modifier: Modifier = Modifier,
readOnly: Boolean = true,
isEnabled: Boolean = true,
isError: Boolean = false,
settingsViewModel: SettingsViewModel = hiltViewModel(),
) {
val items: Map<Int, String> = settingsViewModel.selectableUiModes
var expanded by remember { mutableStateOf(false) }
val selectedItemIndex = settingsViewModel.currentUiMode
var selectedText by remember { mutableStateOf(if (selectedItemIndex.value == null) "" else items[selectedItemIndex.value]) }
val optionList by remember { mutableStateOf(items) }
Column {
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
OutlinedTextField(
isError = isError,
enabled = isEnabled,
modifier = modifier,
readOnly = readOnly,
value = selectedText!!,
onValueChange = {
selectedText = it
},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded
)
}
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
optionList.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
selectedText = selectionOption.value
settingsViewModel.setUiMode(selectionOption.key)
expanded = false
}
) {
Text(text = selectionOption.value)
}
}
}
}
}
}
Why is the value not getting updated for currentUiMode? I don't want to use LiveData for it.