rive-app / rive-app/rive-android

RivePointerInputMode.PassThrough — clickables underneath Rive's layout bounds don't fire onClick

Open
#454 0 comments 0 reactions 0 assignees View on GitHub

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-android 11.4.1, androidx.compose.ui 1.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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.