'React Native: On Android, a button covered by position absolute view can still be tapped

Scenario: I have a red, green and blue box, each having a yellow box touchable inside. Red and blue boxers are siblings. Green box is a child of red box and is position: absolute so that it is overlapping. Using zIndex on parent of green box (i.e. red box) I managed to overlay green box over both red and blue boxes. Each touchable logs its parent's color when pressed.

[This is Android only Issue]

Issue: Tapping touchable on the green box, in an area where green is overlapping with blue, taps the blue box rather than green box despite green being on top.

Expected Behavior: Green box should be tapped since it is on top.

Screenshot: https://i.ibb.co/PgBsHmn/android-issue-with-position-absolute.png

Snack: https://snack.expo.io/@bgcodes/absolute-position-issue-on-android

Code:

import { View, TouchableOpacity, StyleSheet, Text } from 'react-native';

const App = () => {
return (
  <View style={styles.container}>
    <View style={styles.red}>
      <TouchableOpacity onPress={()=>console.log('red')}>
        <View style={styles.yellow} />
      </TouchableOpacity>
      <View style={styles.green}>
        <TouchableOpacity onPress={()=>console.log('green')}>
          <View style={styles.yellow} />
        </TouchableOpacity>
      </View>
    </View>
    <View style={styles.blue}>
      <TouchableOpacity onPress={()=>console.log('blue')}>
        <View style={styles.yellow} />
      </TouchableOpacity>
    </View>
  </View>
)}

export default App;

const styles=StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    flex: 1,
  },
  yellow: {
    height: 90,
    width: 90,
    backgroundColor: 'yellow',
  },
  red: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'red',
    zIndex: 5,
  },
  green: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'green',
    position: 'absolute',
    left: 30,
    top: 50,
  },
  blue: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'blue',
    zIndex: 1,
  },
});

Any Help would be greatly appreciated 🙏



Solution 1:[1]

Ok, turns out as a workaround you can import TouchableOpacity from 'react-native-gesture-handler' and it works fine. But the issue still persists when using TextInput. i.e. If there was a TextInput (instead of Blue box) underneath the overlapping Green box, pressing greenbox would focux on TextInput.

Edit: Workaround for TextInput. You can use pointerEvents prop on TextInput to ignore touch event.

<TextInput pointerEvents={isOverlapped ? 'none' : 'auto'} />

Solution 2:[2]

The rendering is tricky for your feature. Something like that should work (pseudo code)

Right now, your code look like this:

<RedSquareComponent />
<GreenSquare />
<BlueSquare />

In order to have the green square above the blue one, just switch the two.

<RedSquareComponent />
<BlueSquare />
<GreenSquare />

Here it is on snack:

https://snack.expo.io/@sportelli/absolute-position-issue-on-android

Solution 3:[3]

I think that should be good:

import React from 'react';
import { View, TouchableOpacity, StyleSheet, Text } from 'react-native';

const App = () => {
  return (
    <View style={styles.container}>
      <View style={styles.red}>
        <TouchableOpacity
          onPress={() => console.log('red')}
          style={styles.yellow}
        />
      </View>
      <View style={styles.green}>
        <TouchableOpacity
          onPress={() => console.log('green')}
          style={styles.yellow}
        />
      </View>
      <View style={styles.blue}>
        <TouchableOpacity
          onPress={() => console.log('blue')}
          style={styles.yellow}
        />
      </View>
    </View>
  );
};

export default App;

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    flex: 1,
  },
  yellow: {
    height: 90,
    width: 90,
    backgroundColor: 'yellow',
  },
  red: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'red',
    zIndex: 5,
  },
  green: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'green',
    position: 'absolute',
    left: 170,
    zIndex: 6,
  },
  blue: {
    justifyContent: 'center',
    alignItems: 'center',
    height: 100,
    width: 100,
    backgroundColor: 'blue',
    zIndex: 1,
  },
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3