software-mansion / software-mansion/react-native-gesture-handler
[Android] Native views inside GestureDetector stop receiving touches once a transform moves them outside the detector's frame
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 6.8k
- Forks
- 1.1k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 50
Description
[!NOTE]
This issue was authored by AI on behalf of @bartlomiejbloniarz.
Description
On Android, a native view inside a v3 GestureDetector stops receiving touches once a transform moves it outside the detector's frame. In the example below a Switch translated by 160 never toggles, while the same Switch without a detector does. iOS is not affected.
The detector's native frame is the bounding box of its children's layout frames (RNGestureHandlerDetectorShadowNode::layout), and transforms do not affect layout. Android's ViewGroup only dispatches a touch to a child whose bounds contain it, so a touch on the translated Switch is rejected at the detector view and never reaches the Switch. Gestures attached to the detector are not affected, because the orchestrator does its own traversal. Only native children (Switch, and I would expect TextInput, sliders and similar) lose touches.
This is the same root cause as #4250. #4251 made the orchestrator's handler bounds follow the child transform, but native touch dispatch still uses the detector's untransformed frame.
On iOS RNGestureHandlerDetector overrides the overflow inset in updateLayoutMetrics, so hit testing continues outside its bounds. I did not find an Android counterpart.
The example uses a static transform style. The same happens when the transform is animated, for example a draggable card that contains a native control.
https://github.com/user-attachments/assets/1ec96c85-28da-4624-8c82-852fbfad2c2e
In the recording the taps on the Switch inside the detector do nothing, both before and after the Switch without a detector is toggled.
Steps to reproduce
- Render the component below as the app root on Android.
- Tap the first
Switch(insideGestureDetector). It stays off. - Tap the second
Switch(noGestureDetector). It turns on.
import React, { useState } from 'react';
import { StyleSheet, Switch, Text, View } from 'react-native';
import {
GestureDetector,
GestureHandlerRootView,
useTapGesture,
} from 'react-native-gesture-handler';
function TranslatedSwitch({ detector }: { detector: boolean }) {
const [enabled, setEnabled] = useState(false);
const tap = useTapGesture({});
const control = (
<View style={styles.translated}>
<Switch value={enabled} onValueChange={setEnabled} />
</View>
);
return (
<>
<Text>
{detector ? 'Inside GestureDetector' : 'No GestureDetector'}:{' '}
{enabled ? 'ON' : 'OFF'}
</Text>
<View style={styles.row}>
{detector ? (
<GestureDetector gesture={tap}>{control}</GestureDetector>
) : (
control
)}
</View>
</>
);
}
export default function App() {
return (
<GestureHandlerRootView style={styles.screen}>
<TranslatedSwitch detector />
<TranslatedSwitch detector={false} />
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
paddingTop: 90,
paddingHorizontal: 24,
backgroundColor: 'white',
},
row: { height: 90, marginBottom: 24 },
translated: {
width: 100,
height: 70,
justifyContent: 'center',
backgroundColor: '#dbeafe',
transform: [{ translateX: 160 }],
},
});
A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug.
The single-file example above. I ran it as the root of Reanimated's fabric-example app.
Gesture Handler version
3.3.0-nightly-20260821-439ded45d
React Native version
0.87.0
Platforms
Android
JavaScript runtime
Hermes
Workflow
React Native (without Expo)
Architecture
New Architecture (Fabric)
Build type
Debug mode
Device
Android emulator
Device model
Pixel 9 emulator, Android 15 (API 35)
Acknowledgements
Yes
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the single-file example on Android, then read RNGestureHandlerDetectorShadowNode::layout and the Android detector/ViewGroup touch-dispatch path. Compare the behavior with iOS RNGestureHandlerDetector's updateLayoutMetrics overflow handling. Done means transformed native children such as Switch receive touches outside the detector's untransformed frame while detector gestures continue to work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, react-native, typescript
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100