简体   繁体   中英

Jetpack Compose: MutableState<Boolean> not working as intended

In our Android app we want to introduce Compose to a simple debug screen, where we can enable/disable SharedPreferences. I'm trying to get that running using Compose' interface MutableState - but it does not work how I think it does. My plan is to temporarily use MutableState to set a boolean in SharedPreferences (before migrating to DataStore later).

Here is what I had in mind:

private class MyOwnState(startWith: Boolean) : MutableState<Boolean> {
    override var value: Boolean = startWith
    override fun component1(): Boolean = value
    override fun component2(): (Boolean) -> Unit = { value = it }
}

// then, in composable:
var value by remember { MyOwnState(false) }

Of course in real life I would overwrite the getter+setter of the value - but this example is enough, because it does not work. The state change is not propagated and the UI is not updated.

To illustrate this, I but together the code snippets by remember { mutableStateOf(false) } and by remember { MyOwnState(false) } . The first one works (switch is updated), the second one does not.

Full code:

@Composable
fun SomeStateExamples() {
    Column {
        SwitchWorks()
        SwitchDoesNotWork()
    }
}

@Composable
fun SwitchWorks() {
    var value by remember { mutableStateOf(false) }
    Switch(checked = value, onCheckedChange = { value = it })
}

@Composable
fun SwitchDoesNotWork() {
    var value by remember { MyOwnState(false) }
    Switch(checked = value, onCheckedChange = { value = it })
}


private class MyOwnState(startWith: Boolean) : MutableState<Boolean> {
    override var value: Boolean = startWith
    override fun component1(): Boolean = value
    override fun component2(): (Boolean) -> Unit = { value = it }
}

The first switch is togglable, the second one is not:

第一个开关工作,第二个不工作

What am I missing? The MutableState interface is pretty simple, and stable - and I didn't find any extra methods (aka invalidate, notifyListeners, ...) that I need to call.

Thank you for your help!

Adding to Johan's answer, it looks like you also need to implement StateObject to fetch the value and update thd snapshot system. By having a look at SnapshotMutableStateImpl

override var value: T
        get() = next.readable(this).value
        set(value) = next.withCurrent {
            if (!policy.equivalent(it.value, value)) {
                next.overwritable(this, it) { this.value = value }
            }
        }

private var next: StateStateRecord<T> = StateStateRecord(value)

override val firstStateRecord: StateRecord
    get() = next

You will see that using StateObject makes you work with StateRecord s where you store the updatable value, read it and update it.

In your MyOwnState class you have to implement private mutableState value like this:

private class MyOwnState(startWith: Boolean) : MutableState<Boolean> {
    private var _value by mutableStateOf(startWith)
    override var value: Boolean = startWith
        get() = _value
        set(value) {
            _value = value
            field = value
        }
    override fun component1(): Boolean = value
    override fun component2(): (Boolean) -> Unit = { value = it }
}

When you will try to change value inside composable, composition will recompose because you also changed MutableState _value . Read more about how state works in Jetpack Compose here .

Not an answer directly, but looking at how mutableStateOf works, it's also calling createSnapshotMutableState(value, policy) behind the scenes.

So I don't think just inheriting MutableState and changing that will cause Compose to initiate a recomposition and thus updating the UI.

I would probably instead try to pass in the state of the UI from outside as a model with ViewModel or LiveData and mutate that model data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM