Home > Blockchain >  React Native - Pressable is not clickable on Android
React Native - Pressable is not clickable on Android

Time:10-27

What I'm building:

I'm creating a card that will render with a question mark at top of it and when the user press it, the card will flip with animation and show some information.

Code:

From a code perspective it looks like this:
import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import { GlobalStyles } from '../../constants/styles';
import { useTranslation } from 'react-i18next';

export default Card = ({ isSpy, guessingItem, onPress }) => {
  const { t } = useTranslation();

  let currentValue = 0;
  const animatedValue = new Animated.Value(0);
  animatedValue.addListener((v) => (currentValue = v.value));

  const frontInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['0deg', '180deg'],
  });

  const backInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['180deg', '360deg'],
  });

  const frontAnimatedStyles = {
    transform: [{ rotateY: frontInterpolate }],
  };

  const backAnimatedStyles = {
    transform: [{ rotateY: backInterpolate }],
  };

  const flipCard = () => {
    console.log(currentValue);
    if (currentValue >= 90) {
      Animated.spring(animatedValue, {
        toValue: 0,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
      setTimeout(() => onPress(), 600);
    } else {
      Animated.spring(animatedValue, {
        toValue: 180,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
    }
  };

  return (
    <View style={styles.constainer}>
      <Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
        <View style={GlobalStyles.styles.flexbox}>
          <Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
            <FontAwesome name="question" size={160} />
          </Animated.View>
        </View>
      </Pressable>
      <Pressable onPress={flipCard}>
        <View style={GlobalStyles.styles.flexbox}>
          <Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
            <View style={styles.constainer}>
              {!isSpy && (
                <>
                  <FontAwesome name={guessingItem.section.icon} size={60} />
                  <Text style={styles.itemName}>{guessingItem.name}</Text>
                </>
              )}
              {isSpy && (
                <>
                  <FontAwesome name="user-secret" size={60} />
                  <Text style={styles.placeName}>{t('youAreSpy')}</Text>
                </>
              )}
            </View>
          </Animated.View>
        </View>
      </Pressable>
    </View>
  );
};

const styles = StyleSheet.create({
  constainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  innerContainer: {
    height: 300,
    width: 230,
    marginVertical: 50,
    padding: 60,
    borderWidth: 6,
    borderColor: GlobalStyles.colors.primary50,
    borderRadius: 20,
    justifyContent: 'center',
    alignItems: 'center',
    backfaceVisibility: 'hidden',
  },
  innerContainerBack: {
    position: 'absolute',
    right: -115,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  itemName: {
    marginTop: 20,
    fontSize: 18,
    alignItems: 'center',
    color: GlobalStyles.colors.primary50,
  },
  pressed: {},
});

What is an issue:

On-screen below you can see a pressable element with red background. If I click on any part of this red part, the card would flip. If I test it on Android, clicking on anything inside the white border would not trigger a flip. Only clicking outside of that white border but still inside the red background would trigger a flip.

The question is: Why it behaves differently when I am using basic react-native elements? How can I fix that so clicking would always work for the inside click? enter image description here

CodePudding user response:

And you'd better use only one Pressable Component and remove the View component in the Pressable. So my ultimate solution is

import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import { GlobalStyles } from '../../constants/styles';
import { useTranslation } from 'react-i18next';

export default Card = ({ isSpy, guessingItem, onPress }) => {
  const { t } = useTranslation();

  let currentValue = 0;
  const animatedValue = new Animated.Value(0);
  animatedValue.addListener((v) => (currentValue = v.value));

  const frontInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['0deg', '180deg'],
  });

  const backInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['180deg', '360deg'],
  });

  const frontAnimatedStyles = {
    transform: [{ rotateY: frontInterpolate }],
  };

  const backAnimatedStyles = {
    transform: [{ rotateY: backInterpolate }],
  };

  const flipCard = () => {
    console.log(currentValue);
    if (currentValue >= 90) {
      Animated.spring(animatedValue, {
        toValue: 0,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
      setTimeout(() => onPress(), 600);
    } else {
      Animated.spring(animatedValue, {
        toValue: 180,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
    }
  };

  return (
    <View style={styles.constainer}>
      <Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
          <Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
            <FontAwesome name="question" size={160} />
          </Animated.View>
          <Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
            <View style={styles.constainer}>
              {!isSpy && (
                <>
                  <FontAwesome name={guessingItem.section.icon} size={60} />
                  <Text style={styles.itemName}>{guessingItem.name}</Text>
                </>
              )}
              {isSpy && (
                <>
                  <FontAwesome name="user-secret" size={60} />
                  <Text style={styles.placeName}>{t('youAreSpy')}</Text>
                </>
              )}
            </View>
          </Animated.View>
      </Pressable>
    </View>
  );
};

const styles = StyleSheet.create({
  constainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  innerContainer: {
    height: 300,
    width: 230,
    marginVertical: 50,
    padding: 60,
    borderWidth: 6,
    borderColor: GlobalStyles.colors.primary50,
    borderRadius: 20,
    justifyContent: 'center',
    alignItems: 'center',
    backfaceVisibility: 'hidden',
  },
  innerContainerBack: {
    position: 'absolute',
    right: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  itemName: {
    marginTop: 20,
    fontSize: 18,
    alignItems: 'center',
    color: GlobalStyles.colors.primary50,
  },
  pressed: {},
});

CodePudding user response:

I tested this component on my side and it works well. I think there is no problem with the Card component. Please check the parent component of the Card. enter image description here

I only changed "right" value from -115 to 0 in the style of innerContainerBack.

  innerContainerBack: {
    position: 'absolute',
    right: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  }
  • Related