From cdf670624348cedf0403389c05b92701cf50fc7e Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 22 May 2026 15:10:15 -0400 Subject: [PATCH] [Android] Disable pointer events on hidden Swipeable actions container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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. --- .../components/ReanimatedSwipeable/ReanimatedSwipeable.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/react-native-gesture-handler/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx b/packages/react-native-gesture-handler/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx index 3c4d402aec..7f8c2394b7 100644 --- a/packages/react-native-gesture-handler/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx +++ b/packages/react-native-gesture-handler/src/components/ReanimatedSwipeable/ReanimatedSwipeable.tsx @@ -393,6 +393,10 @@ const Swipeable = (props: SwipeableProps) => { 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', }; }); @@ -423,6 +427,9 @@ const Swipeable = (props: SwipeableProps) => { 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', }; });