I have a list of preferences, which i loop over to make a settings screen, and i want to save the state for each of these items, but it isnt saving the state when i restart the app
Code for each function toggle:
@Composable
private fun FunctionToggle(name: String, desc: String, id: String) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val ds = DataStoreKeys(context)
val currentState = ds.getDataStoreValue(id).collectAsState(initial = false).value
var checked by rememberSaveable { mutableStateOf(currentState) }
ListItem(
modifier = Modifier.padding(vertical = 4.dp),
headlineContent = {
Text(
text = name,
style = MaterialTheme.typography.titleLarge,
)
},
supportingContent = {
Text(
text = desc,
style = MaterialTheme.typography.bodyMedium,
)
},
trailingContent = {
Switch(
checked = checked == true,
onCheckedChange = {
checked = it
scope.launch {
ds.saveToDataStore(key = id, value = it)
}
},
)
},
)
class DataStoreKeys(private val context: Context) {
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("userSettings")
}
fun getDataStoreValue(key: String): Flow<Boolean?> {
return context.dataStore.data.map { preferences ->
preferences[booleanPreferencesKey(key)] ?: false
}
}
suspend fun saveToDataStore(key: String, value: Boolean) {
context.dataStore.edit { preferences ->
preferences[booleanPreferencesKey(key)] = value
}
}
}
The problem is here:
val currentState = ds.getDataStoreValue(id).collectAsState(initial = false).value
var checked by rememberSaveable { mutableStateOf(currentState) }
You are collecting the flow from your datastore with default value false
and then remembering the value in checked
variable. The problem is that you don't update checked
when currentState
changes, so it always keeps the default value (until you change it from the switch callback directly).
In my experience, datastore updates are fast enough, so you don't actually need the additional checked
variable and you can use the value from datastore directly. If you'd still rather have that possibility to change the value from the switch callback directly, you can do something like this:
var checked by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
ds.getDataStoreValue(id).collect { checked = it }
}
Switch(
checked = checked,
onCheckedChange = {
checked = it
scope.launch {
ds.saveToDataStore(key = id, value = it)
}
},
)
With this, checked
gets updated every time datastore has a new value as well.