I have a Card + Switch. How do I handle the state ...
# compose
j
I have a Card + Switch. How do I handle the state so it toggles on onClick of the Card, and I can use an onCheckedChange correctly?
Current solution
Copy code
fun SwitchPreference(pref: Preference<Boolean>, text: @Composable () -> Unit) {
    var switchedState = true
    var active by remember {
        switchedState = false
        mutableStateOf(pref.getPref())
    }
    if (active) {
        pref.putPref(active)
    }
    Card(
        Modifier.fillMaxWidth().clickable(onClick = { active = !active}),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(Modifier.padding(16.dp)) {
            text()
            Spacer(Modifier.weight(1f))

            if (switchedState) {
                pref.putPref(active)
            }
            Switch(checked = active, onCheckedChange = {
                active = it
            })
        }
    }
}
a
use
Modifier.toggleable()
should be easier
j
on the card?
a
replace clickable() basically
removed switchState, you don’t need it
j
in that case, if you click directly on the switch, nothing happens
a
Copy code
var toggled by remember{mutableStateOf(true)}
Modifier.toggleable(value = toggled, onValueChange = { toggled = it })
j
mhm, doesnt help a lot ig, the clickable is even shorter tbh
a
actually you seems you don’t need it, Switch already has toggleable inside
Copy code
Card(
        Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(Modifier.padding(16.dp)) {
            text()
            Spacer(Modifier.weight(1f))

            var toggle by remember { mutableStateOf(pref.getPref()) }
            Switch(checked = active, onCheckedChange = {
                toggle = it
                pref.putPref(toggle)
            })
        }
    }
j
... In that case the entire card is not clickable and you have to click exactly on the switch
Copy code
@Composable
fun SwitchPreference(pref: Preference<Boolean>, text: @Composable () -> Unit) {
    var switchedState = true
    var active by remember {
        switchedState = false
        mutableStateOf(pref.getPref())
    }
    Card(
        Modifier.fillMaxWidth().toggleable(active) { active = it },
        shape = RoundedCornerShape(8.dp)
    ) {
        if (switchedState)
            pref.putPref(active)

        Row(Modifier.padding(16.dp)) {
            text()
            Spacer(Modifier.weight(1f))
            Switch(checked = active, onCheckedChange = {
                active = it
            })
        }
    }
}
ended up with this, using and changing the state in both, card and switch
a
try:
Copy code
var toggle by remember { mutableStateOf(pref.getPref()) }
    Card(
        Modifier.fillMaxWidth()
            .toggleable(value = toggle, {
                toggle = it
                pref.putPref(toggle)
            }),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(Modifier.padding(16.dp)) {
            text()
            Spacer(Modifier.weight(1f))

            Switch(checked = active, onCheckedChange = {
                toggle = it
                pref.putPref(toggle)
            })
        }
    }
I don’t get this part of your code:
Copy code
if (switchedState)
            pref.putPref(active)
if it checks switched to true once, it will never turn off
j
then i have 2 putPrefs in 2 different "onToggle"/ "onCheckedChange"
a
it should be fine, your goal is that the card click and switch click works the same
j
switchedState is false for the initial value, true when its switched. (the remember { ... } is only executed on initial composition
so any recomposition (when "active" state changes) will trigger switchedState to be true
yeah it works, was just hoping for a more elegant solution
a
then that’s worse, it calls putPref on every recomposition
Copy code
var toggle by remember { mutableStateOf(pref.getPref()) }
    val onToggle = { 
      toggle = it
      pref.putPref(it)
    }
    Card(
        Modifier.fillMaxWidth()
            .toggleable(value = toggle, onToggle),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(Modifier.padding(16.dp)) {
            text()
            Spacer(Modifier.weight(1f))

            Switch(checked = active, onCheckedChange = onToggle)
        }
    }
j
putPref should be executed on every recomposition recomposition means the "active" state is changed, which is what i want
a
you shouldn’t expect that, the devs say recomposition can happen at any time, you shouldn’t trust it only happens when you change active
✔️ 1
j
true Switching back to local functions then ig