Piotr Prus
07/30/2022, 8:37 PMonValueChangedFinished
when use on slider with steps and snapping. It do not have a value parameter, so I am taking the parameter from my remembered field. The value that is stored in the moment of onValueChangedFinished
is not the value that slider will snap to. It is the value from the moment I released the finger. I do not understand the point of this lambda and I do not know how to properly update the slider value, since I allow only these values from step point, not float from the middle. Code and example in the thread 🧵 👇Piotr Prus
07/30/2022, 8:37 PM@Composable
fun SliderTest() {
val (sliderValue, setSliderValue) = remember { mutableStateOf(0f) }
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Slider(
modifier = Modifier.testTag(SLIDER_TAG).fillMaxWidth(),
value = sliderValue,
valueRange = 0f..list.size.minus(1).toFloat(),
steps = list.size.minus(2),
onValueChange = {
println("AAAA, value change: $it")
setSliderValue(it)
},
onValueChangeFinished = {
println("AAAA, onFinished: $sliderValue")
}
)
}
Piotr Prus
07/30/2022, 8:39 PMAAAA, value change: 2.1914978
AAAA, value change: 2.1914978
AAAA, value change: 2.1767893
AAAA, value change: 2.1058307
AAAA, value change: 2.0159974
AAAA, value change: 2.0033238
AAAA, value change: 2.0
AAAA, onFinished: 2.1914978
I released a finger at 2.19. The value is snapped to 2.0, but the moment lambda is invoked is giving me the “wrong” value.saket
07/31/2022, 2:14 AMRick Regan
07/31/2022, 3:38 AMvar sliderValue by remember { mutableStateOf(0f) }
instead of val (sliderValue, setSliderValue) = remember { mutableStateOf(0f) }
(and in onValueChange do sliderValue = it
instead of setSliderValue(it)
it works as expected.Rick Regan
08/01/2022, 1:01 PMMutableState
are equivalent, but that appears not to be the case here:
1. val sliderValue = remember { mutableStateOf(0f) }
(with println("AAAA, onFinished: ${sliderValue.value}")
) works.
2. var sliderValue by remember { mutableStateOf(0f) }
(with println("AAAA, onFinished: $sliderValue")
) works
3. val (sliderValue, setSliderValue) = remember { mutableStateOf(0f) }
(with println("AAAA, onFinished: $sliderValue")
) doesn’t work.
For #3, the onFinished prints a previous value of the Slider, which is easiest to see when you tap the slider instead of dragging it. I guess the destructuring declaration “captures” an earlier value? I’ve never used this form before -- is that expected?Piotr Prus
08/02/2022, 9:42 AMRick Regan
08/02/2022, 1:28 PMonValueChange
to continuously set the slider value in a mutable state variable but then use onValueChangeFinished
to write that value to a preferences DataStore. For other uses of Sliders I don’t have an onValueChangeFinished
at all.Piotr Prus
08/02/2022, 1:30 PMRick Regan
08/02/2022, 1:32 PMRick Regan
08/02/2022, 5:39 PMSlider
out of the picture and still demonstrates the same phenomenon:
@Composable
fun TwoLambdaTest() {
val (value, setValue) = remember { mutableStateOf(0) }
TwoLambdaComposable(
value = value,
onValueChange = {
println("AAAA, onValueChange, value: $value")
println("AAAA, onValueChange, it: $it")
setValue(it)
},
onValueChangeFinished = {
println("AAAA, onValueChangeFinished: $value")
},
)
}
@Composable
fun TwoLambdaComposable(
value: Int,
onValueChange: (Int) -> Unit,
onValueChangeFinished: () -> Unit,
) {
Button(
onClick = {
onValueChange(Random.nextInt(0, 10))
onValueChangeFinished()
}
) {
Text(text = value.toString())
}
}
This prints
AAAA, onValueChange, value: 0
AAAA, onValueChange, it: 4
AAAA, onValueChangeFinished: 0
AAAA, onValueChange, value: 4
AAAA, onValueChange, it: 8
AAAA, onValueChangeFinished: 4
If I use the two other forms of mutable state it works as expected:
AAAA, onValueChange, value: 0
AAAA, onValueChange, it: 4
AAAA, onValueChangeFinished: 4
AAAA, onValueChange, value: 4
AAAA, onValueChange, it: 8
AAAA, onValueChangeFinished: 8
Rick Regan
08/02/2022, 8:12 PMremember
, I wonder if you could explain why the destructuring declaration form of mutable state behaves differently than the other two “equivalent” forms. Thanks.Zach Klippenstein (he/him) [MOD]
08/03/2022, 7:00 PMval myStateHolder = remember { mutableStateOf(…) }
val myState = myStateHolder.value
val changeMyState = { myStateHolder.value = it }
Look at the implementation – that’s literally exactly what the component functions are doing. That .value
is the thing to look for – that’s the “read” that will trigger whatever scope it executes in to restart when the value changes. Because the read here happens in the composition directly, it will always trigger recomposition when it changes, and anything capturing myState
will capture the value read from the MutableState at the time it was captured.Rick Regan
08/03/2022, 8:29 PMval (value, setValue) ...
does happen again (because setValue(it)
causes recomposition of TwoLambdaTest()
) but since the actual read of the State is not done in the lambda, the initial value it captured from initial composition is all it’s going to see.
But then why, with each click, does the lambda recapture a new value?Rick Regan
08/03/2022, 8:53 PMRick Regan
08/06/2022, 3:48 PM