is it possible to create a Slider component that c...
# compose
m
is it possible to create a Slider component that can update itself while the user interacts with it, but also being updated from
value
when the user isn’t touching it?
s
Doesn't the normal slider have its value hoisted up? You can simply change it somewhere else and it should reflect on the slider itself, no?
m
Well, i need the value to reflect the manual input immediately while dragging, not waiting for the
source of truth
to be updated (source of truth is remote and can be updated from several clients, normally not at the same time)
if I wait for the roundtrip, the movement will be jerky, but if I only use a local state, it wont be updated from when the remote state changes via another client.
s
Sounds like you can still use your local state as the source of truth for the slider, and update this local state whenever a new value comes from the remote source maybe?
m
almost. I can’t update it from the remote source while interacting with it, so i would need to know if the user still have their finger on the slider. (the remote source is slow but it will return the last set value ever 0.5 seconds or so.)
at a minimum I think i would need, dragStart/Dragstop events from the slider
s
Well the thing is, if new data comes from the backend, but the user is currently interacting with it, wouldn’t the new input then override the value and send that new value to the backend? Which means if the backend value comes, and you update the value but it’s currently being touched, it’d just be overridden. This assumes that this is the interaction you want to have with that slider 🤔
m
While interacting with it, I want to send all updates to the backend, so while sliding(lets say its between 1-100), im sending 1,2,3,4,5,6,7… and since the roundtrip is slow, when im dragging it to 8, the server returns “1”, since we have this delay.. that means the component will be update with “1" and then with the users continuous dragging it will jump back to “8”. this patten will repeat an the marker will jump back and forth between the returned value and the dragged value
s
Is it absolutely necessary that you send all the values instead of sending only the last value when you stop interacting with the slider? With that said, even this wouldn’t solve the problem in all edge cases, interesting, I can’t think of a solution atm, maybe someone else has dealt with this problem before?
m
To make the abstract example more “real”. The source of truth is an amplifier (the volume control) so I would like to hear the change while dragging, but even if i wouldn’t need that, how do I know when the user stops interacting with it so that i know that “now” is the time to send a value?
as its implemented now (the slider) I can only assume that the user stops interacting with it by timeout.
s
Slider does have the
onValueChangeFinished
lambda which does exactly that, but as you said, your use case requires the constant changing anyway so this doesn’t help either unfortunately
m
Oh. Missed that lambda. So between first value change and valuechangefinished, i know that the user interacts with it.
s
Yeap
m
It requires a little bit more logic than a “userDragging” lambda would need, but its manageable. Thanks for the
onValueChangedFinished
hint, not sure how i missed it, but i wouldn’t have found it so quickly without you pointing it out. Thanks.
s
Hey I’m happy I could help even a little bit. If you do figure it out and make it work reliably I’d really be curious to hear about it, it sounds like an interesting problem 🤔
m
Copy code
@Composable
fun UpdatingSlider(value: Float, onValueChange: (Float) -> Unit) {
	var innerValue by remember { mutableStateOf(0f) }
	var dragging by remember { mutableStateOf(false) }
	if (!dragging) innerValue = value

	Slider(
		value = innerValue,
		onValueChangeFinished = {
			dragging = false
		},
		onValueChange = {
			dragging = true
			innerValue = it
			onValueChange(it)
		}
	)
}
can be tested by
Copy code
@Preview
@Composable
fun UpdatingSliderPreview() {
	var position by remember { mutableStateOf(0f) }
	UpdatingSlider(position) {
		println(it)
	}
	LaunchedEffect(Unit) {
		var updater = 0f
		while (true) {
			delay(1000)
			position = sin(updater)
			updater += 0.1f
			while (updater > 1f) updater -= 1f
		}
	}
}
s
Nice, this looks cool!