Skip to content

Commit 2b23b88

Browse files
authored
[Android] Reset the native DOWN flag for unmanaged buttons (#4590)
## Description `ButtonViewGroup` tracks whether the native dispatch delivered DOWN for the current gesture and resets it when the handler goes BEGAN. The reset sat behind the `managedHandlerTag` guard, so it only ran for buttons that manage their own handler (`Touchable`, `Pressable`). A button attached through a detector, like `RectButton` with `useNativeGesture`, set the flag on its first touch and never cleared it. From then on the #4441 rule degraded to the mid-gesture check only, and a fling caught on such a button was not cancelled. The reset now runs before the guard, so every handler on the view gets the per-gesture rule. Since #4582 the detector rule already covers this case for detector-attached buttons, so there is no visible change today; this fixes the stale state so the hook's rule holds on its own. ## Test plan <details> <summary>Repro</summary> ```tsx import { useState } from 'react'; import { FlatList, StyleSheet, Text, View } from 'react-native'; import { GestureHandlerRootView, RectButton } from 'react-native-gesture-handler'; export default function App() { const [presses, setPresses] = useState(0); return ( <GestureHandlerRootView style={styles.flex}> <Text style={styles.counter}>{`presses=${presses}`}</Text> <FlatList data={Array.from({ length: 500 }, (_, i) => i)} keyExtractor={(item) => String(item)} renderItem={({ item }) => ( <RectButton style={styles.row} onPress={() => setPresses((count) => count + 1)}> <Text>Row {item}</Text> </RectButton> )} /> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ flex: { flex: 1 }, counter: { padding: 16, paddingTop: 48, fontFamily: 'monospace' }, row: { height: 80, justifyContent: 'center', paddingLeft: 16, borderBottomWidth: 1 }, }); ``` </details>
1 parent 53da095 commit 2b23b88

1 file changed

Lines changed: 11 additions & 5 deletions

File tree

‎packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,17 @@ class RNGestureHandlerButtonViewManager :
762762
}
763763

764764
override fun onHandlerStateChange(handler: NativeViewGestureHandler, newState: Int, prevState: Int) {
765+
if (newState == GestureHandler.STATE_BEGAN) {
766+
// Reset here, not on gesture end: the native DOWN sets the flag even when the orchestrator
767+
// never tracks the handler (disabled button, alpha below the traversal threshold), so a
768+
// terminal state may never come and the stale value would survive. BEGAN precedes both the
769+
// native dispatch of the same DOWN and the sweep that reads the flag.
770+
//
771+
// Runs before the managed guard because the sweep reads the flag for detector-attached
772+
// buttons too, and those manage no handler.
773+
receivedNativeDown = false
774+
}
775+
765776
if (managedHandlerTag == null) {
766777
return
767778
}
@@ -771,11 +782,6 @@ class RNGestureHandlerButtonViewManager :
771782
val localLastEventWasInside = lastEventWasInside
772783

773784
if (newState == GestureHandler.STATE_BEGAN) {
774-
// Reset here, not on gesture end: the native DOWN sets the flag even when the orchestrator
775-
// never tracks the handler (disabled button, alpha below the traversal threshold), so a
776-
// terminal state may never come and the stale value would survive. BEGAN precedes both the
777-
// native dispatch of the same DOWN and the sweep that reads the flag.
778-
receivedNativeDown = false
779785
flushPendingPressOut()
780786
pressDownTime = SystemClock.uptimeMillis()
781787
pressStarted = false

0 commit comments

Comments
 (0)