Is this a legitimate use of `LaunchedEffect` or am...
# compose
m
Is this a legitimate use of
LaunchedEffect
or am I abusing something? I'm trying to detect changes to multiple
States
and make a callback out of them.
Copy code
@Composable
fun FooSlider(
    onStartedSelection: () -> Unit,
    onSelected: (value: Float) -> Unit
) {
    var sliderPosition by remember { mutableStateOf(0f) }
    val sliderInteractions = remember { MutableInteractionSource() }
    val isDragged by sliderInteractions.collectIsDraggedAsState()
    val isPressed by sliderInteractions.collectIsPressedAsState()

    LaunchedEffect(isDragged || isPressed) {
        when {
            isDragged || isPressed -> onStartedSelection()
            else -> onSelected(sliderPosition)
        }
    }

    Slider(
        value = sliderPosition,
        onValueChange = { sliderPosition = it },
        interactionSource = sliderInteractions
    )

}
z
InteractionSource
exposes a
Flow
of `Interaction`s, it would probably be simpler to stick to the flow APIs and collect this flow in a
LaunchedEffect
instead of converting things to `State`s.
👍 1
☝🏼 1
1
There’s an example of this in the docs
m
I thought about that (drawing from what
collectIsDraggedAsState
does), but I'm wondering anyway - is this how you change
State
change to
event
or not really.
z
It’s one way, I’m not sure if it’s the best way (esp since
LaunchedEffect
launches a new coroutine every time, but you don’t need a coroutine for these non-suspending callbacks). Another way would be to use
snapshotFlow
.
🙏 1
Using
LaunchedEffect
also has the disadvantage of requiring recomposition to signal the state change to your callback. Using
snapshotFlow
wouldn’t.
Also, using
LaunchedEffect
like this means your callbacks won’t get invoked until after the composition is applied, which I think means that there will be a frame that gets rendered between when the state actually changes (which invalidates recompose scopes reading it, triggering recomposition) and the frame that will end up rendering any state changes made by your callback, which could result in visual flicker. That’s probably the more important reason to not use this technique. I’m not sure if the
snapshotFlow
method would have this problem, since the flow logic would run as soon as the snapshot that changes the underlying state was committed. I think it still might, because if the underlying state is changed outside a snapshot, the changes would only be seen when the global snapshot is applied which would only happen when compose is preparing the next frame, at which point it would be too late to make state changes that would affect that frame. If the underlying state changes are being performed inside an explicit snapshot though, it might work. I’d need to actually try it to see.
RippleIndication
uses the snapshotFlow approach though so it’s probably fine.
m
You know your way around compose 😄, thank you so much