I am trying to intercept and consume all click events at the parent level in Jetpack Compose so that child composables (like a Button) do not receive clicks. However, even after calling consume() in awaitPointerEventScope, the child still receives the click event.
Here is my code:
@Composable
fun ParentConsumesClick() {
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
Log.d("Click", "Consumed in Parent")
event.changes.forEach { it.consume() } // Consume touch event
}
}
}
.background(Color.Gray),
contentAlignment = Alignment.Center
) {
Button(onClick = { Log.d("Click", "Child clicked") }) {
Text("Click Me")
}
}
}
Expected Behavior:
"Consumed in Parent"
."Child clicked"
should not be logged).Actual Behavior:
"Consumed in Parent"
is logged (showing that the parent receives the event)."Child clicked"
.You need to consume down event for clickable Modifier or Button to not receive click events.
Also since gestures propagate from child to parent you need to change pass from Main to Initial for parent to receive touch first.
You can check out this answer for more details about how gesture system works in Jetpack Compose.
Create a Modifier such as
private fun Modifier.customTouch(
pass: PointerEventPass,
onDown: () -> Unit = {},
onUp: () -> Unit = {}
) = this.then(
Modifier.pointerInput(pass) {
awaitEachGesture {
val down = awaitFirstDown(pass = pass)
down.consume()
onDown()
val up = waitForUpOrCancellation(pass)
if (up != null) {
onUp()
}
}
}
)
And apply this Modifier to parent such as
@Preview
@Composable
fun ParentConsumesClick() {
val context = LocalContext.current
Box(
modifier = Modifier
.fillMaxSize()
.customTouch(
pass = PointerEventPass.Initial,
onDown = {
Toast
.makeText(context, "Parent Touched", Toast.LENGTH_SHORT)
.show()
},
onUp = {
Toast
.makeText(context, "Parent Up", Toast.LENGTH_SHORT)
.show()
}
)
.background(Color.Gray),
contentAlignment = Alignment.Center
) {
Button(onClick = {
Toast.makeText(context, "Child clicked", Toast.LENGTH_SHORT).show()
}) {
Text("Click Me")
}
}
}
Also if you want to apply ripple on this click you can add InteractionSouce and Modifier.indication.