Skip to content

[Android] Disable pointer events on hidden Swipeable actions container#4192

Open
jamesacklin wants to merge 1 commit into
software-mansion:mainfrom
jamesacklin:fix/swipeable-pointer-events-android
Open

[Android] Disable pointer events on hidden Swipeable actions container#4192
jamesacklin wants to merge 1 commit into
software-mansion:mainfrom
jamesacklin:fix/swipeable-pointer-events-android

Conversation

@jamesacklin
Copy link
Copy Markdown

Description

The left/right action containers in ReanimatedSwipeable are absolute-fill
overlays that animate to opacity: 0 when not revealed. On Android, an
opacity-0 view still receives touches, so the hidden side stays on top in
z-order and swallows taps that should reach the visible side's actions
(or the row content itself). A common repro is a quick-action button
exposed by a swipe: the button visibly responds to press feedback but the
onPress never fires because the opposite-side container intercepts it.

This adds a matching pointerEvents toggle to leftActionAnimation and
rightActionAnimation, so each container becomes 'none' alongside its
opacity going to 0, and switches back to 'auto' once revealed. iOS and
web were not affected by the original bug, but the toggle is harmless on
those platforms (an opacity-0 view there is already non-interactive).

Fixes #3223.

Test plan

  • Carried as a local patch against react-native-gesture-handler@2.28.0
    in our app for the last few weeks. Before the patch, Android taps on a
    swipe-revealed action were dropped intermittently; after the patch,
    every tap fires on the first try. iOS behavior was unchanged.
  • Manual repro for reviewers: in apps/common-app, open a Swipeable
    example, swipe a row to reveal an action, and tap the action on
    Android — the action should fire on the first tap.

## Description

The left/right action containers in `ReanimatedSwipeable` are absolute-fill
overlays that animate to `opacity: 0` when not revealed. On Android, an
opacity-0 view still receives touches, so the hidden side stays on top in
z-order and swallows taps that should reach the visible side's actions
(or the row content itself). A common repro is a quick-action button
exposed by a swipe: the button visibly responds to press feedback but the
`onPress` never fires because the opposite-side container intercepts it.

This adds a matching `pointerEvents` toggle to `leftActionAnimation` and
`rightActionAnimation`, so each container becomes `'none'` alongside its
opacity going to 0, and switches back to `'auto'` once revealed. iOS and
web were not affected by the original bug, but the toggle is harmless on
those platforms (an opacity-0 view there is already non-interactive).

Fixes software-mansion#3223.

## Test plan

- Carried as a local patch against `react-native-gesture-handler@2.28.0`
  in our app for the last few weeks. Before the patch, Android taps on a
  swipe-revealed action were dropped intermittently; after the patch,
  every tap fires on the first try. iOS behavior was unchanged.
- Manual repro for reviewers: in `apps/common-app`, open a `Swipeable`
  example, swipe a row to reveal an action, and tap the action on
  Android — the action should fire on the first tap.
Copilot AI review requested due to automatic review settings May 22, 2026 19:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR aims to prevent hidden swipe action containers from intercepting taps on Android by toggling touch handling based on the left/right “shown” progress.

Changes:

  • Added pointerEvents toggling to the left actions animated style when actions are hidden vs. visible.
  • Added pointerEvents toggling to the right actions animated style when actions are hidden vs. visible.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 393 to 401
const leftActionAnimation = useAnimatedStyle(() => {
return {
opacity: showLeftProgress.value === 0 ? 0 : 1,
// Without this, Android keeps the absolute-fill left actions container
// as a touch target even at opacity 0, swallowing taps that should reach
// the row content or the opposite-side actions below in z-order.
pointerEvents: showLeftProgress.value === 0 ? 'none' : 'auto',
};
});
Comment on lines 427 to 434
const rightActionAnimation = useAnimatedStyle(() => {
return {
opacity: showRightProgress.value === 0 ? 0 : 1,
// See note on leftActionAnimation: needed so the hidden right actions
// container doesn't intercept taps on the visible left actions on Android.
pointerEvents: showRightProgress.value === 0 ? 'none' : 'auto',
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Swipeable renderLeftActions button not firing

2 participants