Question : I'm using compose and listening to the ...
# compose
a
Question : I'm using compose and listening to the interactionSource in my button to change colors on the on pressed. However if I tap gently I get a ripple, and if I press a little harder I get the on pressed state change ... What's with this lag ? Is this a known bug or just how compose works ?
l
collectIsPressedAsState
will not change if the press / release happens within the same frame / composition - as any changes that respond to this would then be shown after the press had already stopped. Ripples are different because they are launched in response to a press and can last longer than the press itself. You can manually observe the flow inside if you want something similar
a
Copy code
val clickFlow = remember { MutableSharedFlow<ButtonClickState>() }
val isClickPressed = clickFlow.collectAsState(initial = ButtonClickState.Released).value == ButtonClickState.Pressed

val backgroundColor =
    if (isClickPressed && state == ButtonState.Enabled) {
        pressedBackgroundColor
    } else {
        if (isDarkTheme) defaultBackgroundDarkColor else defaultBackgroundLightColor
    }

androidx.compose.material.Button(
    onClick = { onClick.takeIf { state == ButtonState.Enabled }?.invoke() },
    modifier = modifier
        .requiredHeightIn(min = 48.dp)
        .pointerInput(Unit) {
            this.detectTapGestures(
                onPress = {
                    clickFlow.emit(ButtonClickState.Pressed)
                    awaitRelease()
                    clickFlow.emit(ButtonClickState.Released)
                }
            )
        },
Thanks for the response @Louis Pullen-Freilich [G] I tried the above and it doesn't seem to trigger my onPress
l
I meant you can directly use
InteractionSource.interactions
Copy code
LaunchedEffect(interactionSource) {
            interactionSource.interactions.collect { interaction ->
                when (interaction) {
                    is PressInteraction.Press -> /* do something*
...
a
ah okay, I saw this as well in the api, but I didn't use this with a LaunchEffect. I guess this means I will still need to expose some mutableState to the launch effect on the press event
merci ! @Louis Pullen-Freilich [G]
l
You will have the same problem with
mutableState
as with
collectIsPressedAsState
though - if you change it within a composition, the updates will be batched and ignored. So you’ll need to launch an animation if you want a pressed effect to show for longer than the tap
a
hmm you're right, I just tested it, and I now realize this impl is exactly what
collectIsPressedAsState
does
I only want to show a background color change for my button, upon tap
l
Well, how long for? If the tap finishes instantaneously, then there won't be time to draw a frame before the user stops the gesture - so you don't want to draw a change in that case unless you have some minimum duration you want to show effects for
a
Hmm, that's a good point. Which is why ripple works, the animation is longer than the tap itself. Ah ! I get what you mean now
I guess, for my case then, it would be suffice to show the pressed state as an animation set to run for x amount of time before going to it's next state
yes this worked
posting an example for others reference
Copy code
setContent {
    val interactionSource = remember { MutableInteractionSource() }
    var backgroundColor by remember { mutableStateOf(Color.Red) }
    LaunchedEffect(interactionSource) {
        interactionSource.interactions.collect { interaction ->
            when (interaction) {
                is PressInteraction -> {
                    backgroundColor = when (interaction) {
                        is PressInteraction.Press -> Color.Blue
                        else -> {
                            delay(500L)
                            Color.Red
                        }
                    }
                }
            }
        }
    }
    ButtonStatesTheme {
        Surface {
            Box(Modifier.fillMaxSize(), Alignment.Center) {
                Button(
                    colors = ButtonDefaults.buttonColors(backgroundColor = backgroundColor),
                    onClick = { /*TODO*/ },
                    content = { Text("Clicked") },
                    interactionSource = interactionSource,
                )
            }
        }
    }
}