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

Open
#4,529 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Platform: Android
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
  1. Render the component below as the app root on Android.
  2. Tap the first Switch (inside GestureDetector). It stays off.
  3. Tap the second Switch (no GestureDetector). 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.