rive-app / rive-app/rive-android
RivePointerInputMode.PassThrough — clickables underneath Rive's layout bounds don't fire onClick
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 538
- Forks
- 66
- PR merge metrics
- No merged PRs in 30d
Description
Summary
With Rive() in PassThrough mode as an overlay above sibling composables, any Button / IconButton whose position overlaps Rive's layout bounds doesn't receive onClick, even though the kdoc says PassThrough is the right mode for "an overlay with transparent sections that should allow pointer events through."
Versions
rive-android11.4.1,androidx.compose.ui1.10.5- Samsung Galaxy S25 (SM-S931U1)
Repro
Box(Modifier.fillMaxSize()) {
// Underlay with two buttons at different positions
Column(Modifier.fillMaxSize()) {
Button(
onClick = { Log.d("test", "top fired") },
modifier = Modifier.align(Alignment.Start),
) { Text("Top button") }
Spacer(Modifier.weight(1f))
Button(
onClick = { Log.d("test", "bottom fired") },
) { Text("Bottom button") }
}
// Rive overlay with bounds only covering the bottom half
Rive(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.5f)
.align(Alignment.BottomCenter),
pointerInputMode = RivePointerInputMode.PassThrough,
// ...
)
}
- Top button (outside Rive's bounds): fires ✓
- Bottom button (inside Rive's bounds): does NOT fire ✗
Per the PassThrough kdoc and Compose's PointerInputFilter.shareWithSiblings kdoc, the bottom button should fire. It doesn't.
Additional diagnostic: applying graphicsLayer { scaleX = 0.5f; scaleY = 0.5f; clip = true; shape = CircleShape } to Rive's modifier does shrink the failing region, but it follows the scaled rectangular layout bounds — not the CircleShape. Useful for pinpointing the problem but doesn't rescue the overlay use case.
Suggested fix
Add a mode that skips the PointerInputFilter entirely — the Compose equivalent of .allowsHitTesting(false) that rive-ios's own DrillLessonMascotView.swift uses:
enum class RivePointerInputMode { Consume, Observe, PassThrough, None }
// In Rive():
val finalModifier = when (pointerInputMode) {
RivePointerInputMode.None -> modifier
else -> modifier.then(passThroughInputModifier)
}
With None, Rive is a pure drawing layer — Compose hit-testing walks normally and sibling-underneath clickables fire. Matches the iOS pattern.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in the Rive() implementation and trace passThroughInputModifier and RivePointerInputMode handling. Reproduce the overlay case from the issue, then verify that a non-pointer-input mode leaves the drawing layer visible while the underlying Button receives clicks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 56/100