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:
- Clicking anywhere inside the Box should log
"Consumed in Parent"
. - The Button inside should not receive any clicks (i.e.,
"Child clicked"
should not be logged).
Actual Behavior:
"Consumed in Parent"
is logged (showing that the parent receives the event).- But the Button still receives the click event and logs
"Child clicked"
.
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:
- Clicking anywhere inside the Box should log
"Consumed in Parent"
. - The Button inside should not receive any clicks (i.e.,
"Child clicked"
should not be logged).
Actual Behavior:
"Consumed in Parent"
is logged (showing that the parent receives the event).- But the Button still receives the click event and logs
"Child clicked"
.
1 Answer
Reset to default 0You 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.
Button
or use aSurface
Composable. – BenjyTec Commented 6 hours ago