Repository navigation
[Android] Pressable fires onPress when the touch only stops a fling #4432
Description
Description
On Android, Pressable fires onPress when the touch was only meant to stop a fling.
Put a finger down on a list row while the list is still decelerating, then lift it without moving: the row under the finger receives a press. In a shop list that means opening a product page the user never tapped.
This is a regression introduced with the v3 Pressable. Using LegacyPressable, or passing any relation prop (simultaneousWith / requireToFail / block, which routes the component to StatefulPressable), makes the phantom press go away.
Root cause
Pressable without relation props renders PressableWithTouchable, which handles the press natively through ButtonViewGroup. The NativeViewGestureHandler the button manages for itself is attached with ACTION_TYPE_NONE:
When a native view grabs the touch lock — Android's ScrollView intercepts the ACTION_DOWN while its scroller is still running and calls requestDisallowInterceptTouchEvent(true) up the tree — RNGestureHandlerRootHelper.requestDisallowInterceptTouchEvent() reacts with GestureHandlerOrchestrator.cancelAllLegacyHandlers(), which only cancels handlers whose action type is ACTION_TYPE_JS_FUNCTION_OLD_API, ACTION_TYPE_JS_FUNCTION_NEW_API, ACTION_TYPE_REANIMATED_WORKLET or ACTION_TYPE_NATIVE_ANIMATED_EVENT.
The button's handler is none of those, so it survives, reaches STATE_END on the finger lift and dispatches the press. The framework's ACTION_CANCEL doesn't save it either, since RNGH delivers touches itself and ignores onInterceptTouchEvent.
The v2 Pressable was built on GestureDetector, so its handlers carried a JS action type and were cancelled by that same path — hence the regression.
LegacyPressable and StatefulPressable are unaffected for the same reason.
Steps to reproduce
- Render a list (
FlatList,FlashListor a plainScrollViewfromreact-native) whose rows are wrapped inPressablefromreact-native-gesture-handler. - Fling the list hard so it keeps scrolling on its own.
- While it decelerates, put a finger down on a row to stop the scroll.
- Lift the finger without moving it.
Expected: nothing happens, the touch only stopped the scroll.
Actual: onPress fires for the row under the finger.
Repro
import React, { useState } from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';
import { GestureHandlerRootView, Pressable } from 'react-native-gesture-handler';
const DATA = Array.from({ length: 80 }, (_, index) => `Item ${index}`);
export default function App() {
const [pressed, setPressed] = useState<string>('—');
return (
<GestureHandlerRootView style={styles.root}>
<View style={styles.banner}>
<Text style={styles.bannerText}>last onPress: {pressed}</Text>
</View>
<FlatList
data={DATA}
keyExtractor={(item) => item}
renderItem={({ item }) => (
<Pressable style={styles.row} onPress={() => setPressed(item)}>
<Text>{item}</Text>
</Pressable>
)}
/>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
root: { flex: 1 },
banner: { padding: 16, backgroundColor: '#eee' },
bannerText: { fontWeight: 'bold' },
row: { padding: 24, borderBottomWidth: 1, borderBottomColor: '#ddd' },
});Fling the list, stop it with a finger, lift without moving: the banner shows the row that was under the finger. Swap the import for LegacyPressable and it stays at —.
Gesture Handler version
3.2.1
React Native version
0.86.2
Platforms
Android
JavaScript runtime
Hermes
Workflow
Using Expo Prebuild or an Expo development build
Architecture
New Architecture (Fabric)
Build type
Debug mode
Device
Real device
Activity
github-actions commentedon Aug 17, 2026
Hey! 👋
The issue doesn't seem to contain a minimal reproduction.
Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?
github-actions commentedon Aug 17, 2026
Hey! 👋
It looks like you've omitted a few important sections from the issue template.
Please complete A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug. section.
TomCorvus commentedon Aug 17, 2026
Hi! I’m experiencing a very similar issue with
Pressablein RNGH 3.2.1 on Android.In my case, the problem also occurs with a
SectionList: when I put my finger on aPressableand start scrolling the list,onPresscan still be triggered even though the gesture was intended to scroll the list. I’m also seeing the same kind of behavior withonLongPress— if the finger remains on the item long enough while scrolling, the long press can be triggered as well.Interestingly, switching from RNGH’s
Pressableto React Native’s built-inPressablecompletely fixes the issue for me.#4433. Would that fix also cover this case, particularly the
SectionListscenario and theonLongPressbehavior, or is it specifically targeting the phantomonPresswhen stopping a fling?Thanks!
m-bert commentedon Aug 18, 2026
Hi @huextrat! Thank you for the report and the PR - the analysis in both is spot on, and the issue writeup made the root cause immediately clear.
We'd like to handle this differently though. When the disallow request reaches the root, there's no way to tell whether the caller actually took the event or just doesn't want its ancestors to intercept -
react-native-pager-viewdoes the latter onACTION_DOWN, so the approach from #4433 would break buttons inside nested pagers (the case #4367 fixed).I've opened #4441, hope you don't mind 😅
m-bert commentedon Aug 18, 2026
Hi @TomCorvus! Your case turned out to be a third variant of the same problem - there the
ScrollViewtakes the touch over mid-gesture, when the finger starts dragging from a row. We reproduced both symptoms with aSectionListand the fix covers them too: noonPresson lift after a drag, noonLongPresswhile scrolling. It would be great if you could confirm that applying #4441 works.huextrat commentedon Aug 18, 2026
Thanks @m-bert! I hadn't considered the
react-native-pager-viewcase, you're right that a disallow request reaching the root is ambiguous and it would have regressed #4367.Happy to see it handled properly, no problem at all about #4441 taking over from #4433.
Thanks for picking this one up so quickly again 👍
[Android] Fix buttons firing press events when a scroll takes over th…
aledebla03 commentedon Aug 28, 2026
We're running 3.2.1 with
d3547acdapplied as a patch (verified byte-identical to the upstream commit). The three variants discussed in this thread are fixed, but the original one from the issue description still reproduces on a signed release build:onPressfires for the row.It's intermittent, not deterministic — roughly 1 in 8 attempts in an automated run. Controlled comparison on the same physical device (Xiaomi M2101K9G, Android 13), same gesture script, same app:
d3547acdReproduced on both a plain RN
ScrollViewand a FlashList v2 list, withTouchable(notPressable) as the row wrapper — so it isn't container-specific, and it may be worth checking whether theTouchablepath is covered by the fix the same wayPressableWithTouchableis.One observation from reading the patch, in case it helps: AOSP
ScrollView.onTouchEventdoes callrequestDisallowInterceptTouchEvent(true)onACTION_DOWNwhen!mScroller.isFinished(), and RN's non-paging fling usesmScrollervia reflection — sonativeTouchGrabRequestedshould arm, and withgrabbedMidGesture == falsethe!receivedNativeDownclause should cancel the handler. Empirically it doesn't, and we haven't instrumented far enough to say why.Happy to put together a minimal repro if that would help.
m-bert commentedon Sep 7, 2026
Hi @aledebla03! It would be great if you could prepare a repro 😄
chore(mobile): bump react-native-gesture-handler to 3.3.0
chore(mobile): bump react-native-gesture-handler to 3.3.0
feat(mobile): use Gesture Handler Touchable for presses (#1537)