Manu Eder
02/24/2021, 11:30 AMstate: T, onChange: (T) -> Unit
) instead of a single parameter (stateD: MutableState<T>
). E.g. why is the Checkbox2
-example below promoted over Checkbox1
. (I know the two are subtly different in when the state that the modification is based on is read.)
More generally, I think what I was expecting to find, but haven't seen yet is some standard way to handle what I expect to be a common case of creating some "widget" whose entire state can be described by some (immutable data) class and where "subwidgets" modify part of that state. I.e. I guess I'm looking for a blessed function:
(MutableState<T>, Lens<T, S>) -> MutableState<S>
or, with automatic creation of Lenses (https://www.reddit.com/r/Kotlin/comments/4i5wsp/lenses_for_kotlin/):
(MutableState<T>, KProperty1<T, S>) -> MutableState<S>
.
Does something like this exist? Is there a good reason why it shouldn't?
Checkbox1
and Checkbox2
-example:
@Composable
fun Checkbox1(stateD: MutableState<Boolean>) {
var state by stateD
Button(onClick = {state = !state}) {
Text( if (state) "on" else "off")
}
}
@Preview
@Composable
fun UseCheckbox1() {
val stateD = remember { mutableStateOf(false) }
Checkbox1(stateD)
}
@Composable
fun Checkbox2(stateVal: Boolean, stateChanger: (Boolean) -> Unit ) {
Button(onClick = {stateChanger(!stateVal)}) {
Text( if (stateVal) "on" else "off")
}
}
@Preview
@Composable
fun UseCheckbox2() {
val stateD = remember { mutableStateOf(false) }
var state by stateD
Checkbox2(stateVal = state, stateChanger = {state = it} )
}
allan.conda
02/24/2021, 12:11 PMManu Eder
02/24/2021, 12:32 PMremember { mutableStateOf( ... ) }
), but instead will want to pass it down.
The documentation promotes passing down a value and a setter (which is very similar to a getter and a setter).
I would like to be able to just say "use this field of this variable that I already have" (which was probably passed down to me, and might again be a field of some other variable), without having to write getters and setters all the time.
So, I'd like to be able to have something like
data class TwoBools( val one: Boolean, val two: Boolean )
@Composable fun TwoCheckboxes( state: GetSet<TwoBools> ) {
Checkbox( state.field(TwoBools::one) )
Checkbox( state.field(TwoBools::two) )
}
@Composable fun Checkbox( state: GetSet<Boolean> ) {
...
}
field
would be a member/extension function of GetSet<T>. And GetSet<T>
might or might not be equal to MutableState<T>
.field
would be the aforementioned
(GetSet<T>).(KProperty1<T, S>) -> GetSet<S>
field
. This just seems like such an obvious pattern that I felt it should probably exist somewhere already. Also, I don't understand the internals of compose and don't know what kind of performance implications the different ways of doing this might have.
Should GetSet<T>
be an interface (i.e. = MutableState<T>
?), should it be
data class GetSet<T>(get: () -> T, set: (T) -> Unit)
, should it be
data class GetSet<T>(get: T, set: (T) -> Unit)
,
etc...?TwoCheckboxes
code above would look like this:
data class TwoBools( val one: Boolean, val two: Boolean )
@Composable fun TwoCheckboxes( state: TwoBools, stateSetter: (TwoBools) -> Unit ) {
Checkbox( state.one, { stateSetter(state.copy(one=it)) } )
Checkbox( state.two, { stateSetter(state.copy(two=it)) } )
}
@Composable fun Checkbox( state: Boolean, stateSetter: (Boolean) -> Unit ) {
...
}
, which isn't a lot longer, but does get kinda repetitive, and the field name is referenced twice instead of once...)Albert Chang
02/24/2021, 2:13 PMMutableState
or something like that. LazyListState
is a good example. It is essentially a mutable state.Manu Eder
02/24/2021, 3:25 PMMutableState<T>
is an interface. Anything that you can do with currentValue
and lambdaFun
you can do with `MutableState<T>`:
object : MutableState<Boolean> {
override operator fun component1() = currentValue
override operator fun component2() = lambdaFun
override var value : Boolean
get() = currentValue
set(newVal) = lambdaFun(newVal)
}
(And you can make this less verbose if you plan to do it a lot.)
I noticed LazyListState as one of the few examples where both getter and setter are passed as one variable. I was just wondering why it wasn't using something like MutableState<ImmutableLazyListState>
, i.e. why there wasn't some kind of standardized way of doing what LazyListState does.Albert Chang
02/25/2021, 1:21 AMMutableState
is an interface, it is the SnapshotMutableStateImpl
class that actually enables the Recomposer
to subscribe to changes of the value (the subscription relies on the Snapshot
mechanism). You own implementation of MutableState
won't have this ability thus automatic recomposition will not work. You can verify this yourself.
Besides LazyListState
, essentially the return types of all remember*State()
functions are mutable states, such as ScaffoldState
, ScrollState
, etc. They contain relatively complex logics and provide the user with extra methods.