I got a state holder class like this ```@Stable pr...
# compose
s
I got a state holder class like this
Copy code
@Stable
private class SomeStateHolder<T>() {
    var value: T by mutableStateOf(...)
    var someFlag: Boolean by mutableStateOf(false)
}
I want to wire things up internally so that
someFlag
can be automatically also observe the changes to
value
, and if there is a change to it, have it reset back to
false
. That would be the functionality of what this should be doing:
Copy code
@Stable
private class SomeStateHolder<T>(
    coroutineScope: CoroutineScope,
) {
    var value: T by mutableStateOf(...)
    var someFlag: Boolean by mutableStateOf(false)
    init {
        coroutineScope.launch {
            snapshotFlow { value }.drop(1).collectLatest {
                someFlag = false
            }
        }
    }
}
But I am not sure if passing a coroutineScope to my state holder like that to do the observation on
init
is the way to go. Does anyone have any other thoughts on this?
a
is the code correct? where did
showErrorMessage
come from?
s
Thanks, fixed. It was a copy-paste error. I was trying to simplify my code snippet from my original problem to as barebones as I could in order to make this question easier to discuss outside of the context of what I am trying right now.
a
can u explain what you are trying to do? why not expose the value directly?
ok, took me a while but i understand (very hard to understand this code).
because it's so hard to understand what's going on (ultra generic naming + T), i would keep a state object which just controls the state you want to read (someFlag) and have someone else update it. you can do it with derived state of if u need to combine multiple states together
s
What do you mean by exposing the value directly? Which value are we talking about here? What I was trying to do is have some sort of holder for some input, which can also store internally the input itself (aka
value: T
) and to have a boolean storing information on if we want to show the error message on this input or not. This would practically be used in some forms, where I do want to know if the value is "invalid", but I do not want to show the fact that it has an error yet, until the "Continue" button is pressed. At that point the flag would be turned to true to show the error. Then I want to automatically be able to change the input, and have the error message hide, no matter if the input is still invalid, since they are actively trying to fix it. Hence the snaphshot flow, clearing that flag after any change on the input itself.
you can do it with derived state of if u need to combine multiple states together
I would not be able to use derivedStateOf because I do want to update that flag manually as well, and not have it only be a derived value of other things.
and have someone else update it.
Yes, this is what I am leaning towards as well, perhaps with an accompanying
rememberSomeStateHolder()
which wires things up for me.
o
Maybe just a custom setter method? 🤔
Copy code
@Stable
private class SomeStateHolder<T>() {
    var value: T by mutableStateOf(...)
        private set
    var someFlag: Boolean by mutableStateOf(false)

    fun setValue(value: T) {
        this.value = value
        someFlag = false
    }
}
s
Mm yeah, that might just work, let me play with it a bit. Thanks for the idea!
a
Another pattern you could try is a combined private state holder that links the
value
and
someFlag
together in the same object:
Copy code
@Stable
class InvalidCondition<T>(
    val value: T,
) {
    var someFlag: Boolean by mutableStateOf(false)
        private set

    fun showInvalid() {
        someFlag = true
    }
}

@Stable
class SomeStateHolder<T> {
    var condition: InvalidCondition<T> by mutableStateOf(...)
        private set

    fun setValue(value: T) {
        condition = InvalidCondition(value) // updating condition changes some flag since it is a new object
    }
}
So you can capture the idea of “flag tied to a specific version of the input” by the lifetime of the object representing that version of the input
thank you color 1
s
Fun fact Oleksandr, the suggestion of
Copy code
var value: T by mutableStateOf(initialValue)
    private set

  fun setValue(newValue: T) {
    someFlag = false
    value = newValue
  }
fails with compilation error:
Copy code
Platform declaration clash: The following declarations have the same JVM signature (setValue(Ljava/lang/Object;)V):
    fun `<set-value>`(`<set-?>`: T): Unit defined in com...
    fun setValue(newValue: T): Unit defined in com...
So this works, but you need a more unique name 😄 For your suggestion Alex, then I will end up holding my
InvalidCondition
state object inside a
MutableState
, but then it internally will also contain the
someFlag
also in a
MutableState
. Will that be what I actually want here? Feels like the wrong thing to do in a way
a
Maybe, maybe not. It creates multiple points of mutability, which can be confusing, but this feels like a case where it could make behavior clearer: you can scope state updates to the lifecycle of a particular object that creates a sort of “session”: by creating a new object, you can “re-initialize” the state because you’re creating a new session. Since it’s multiple layers of snapshot-state aware objects, it will work fine from a recomposing perspective so you won’t run into the main issue discussed in https://blog.zachklipp.com/two-mutables-dont-make-a-right/
thank you color 1
s
Yep that's exactly the post I was thinking about, but what you say makes sense. Thanks a lot for your help 😊