Search code examples
androidreact-nativemarginreact-native-flatlisttouchableopacity

Problem with TouchableOpacity, negative margin and Android - React Native


I'm with a problem with TouchableOpacity and Negative Margins inside a FlatList. On iOS works well, but on Android, when I click at the TouchableOpacity in the front of other TochableOpacity, the TouchableOpacity from behind fires. I don't know how to solve this.

iOS Image

I clicked at "Proposta 70" but fires "Proposta 78" from behind. Android Image

The FlatList code

<View style={styles.containerList}>
    <FlatList
      data={proposalsList}
      keyExtractor={item => item.proposta_id}
      renderItem={({ item, index }) => (
        <RenderItem
          item={item}
          index={index}
          isLoweredCard={
            openedCardIndex !== null && index === openedCardIndex + 1
          }
          changeOpenedCardIndex={changeOpenedCardIndex}
        />
      )}
      refreshing={loading}
      onRefresh={() => getProposalsAndNotifications()}
    />
  </View>

The RenderItem code

<TouchableOpacity
  style={styles.container(index, isLoweredCard)}
  onPress={() => changeOpenedCardIndex(index)}
>
  <>
    <View
      style={[
        styles.lineContainer,
        { marginBottom: metrics.padding * 1.5 },
      ]}
    >
      <View
        style={{
          width: '50%',
        }}
      >
        <Text style={styles.proposalId}>
          {`Proposta ${item.proposta_id}`}
        </Text>
        <Text style={styles.proposalDate}>
          {dayjs(item?.proposta_data_criacao).format('DD.MM.YYYY')}
        </Text>
      </View>
      <View
        style={{
          flex: 1,
          justifyContent: 'center',
          alignItems: 'flex-end',
        }}
      >
        <View style={styles.statusContainer}>
          <Text style={{ fontSize: wp(4), fontWeight: 'bold' }}>
            <TypeStatus status={item?.proposta_status} />
          </Text>
        </View>
      </View>
    </View>
    <View style={styles.lineContainer}>
      <Text style={styles.proposalDetailLabel}>Valor solicitado</Text>
      <Text style={styles.proposalDetailValue}>
        {formatCurrency(item?.proposta_valor_financiado)}
      </Text>
    </View>
    <View style={styles.lineContainer}>
      <Text style={styles.proposalDetailLabel}>Valor liberado</Text>
      <Text style={styles.proposalDetailValue}>
        {formatCurrency(item?.proposta_valor_financiado)}
      </Text>
    </View>
    <View style={styles.lineContainer}>
      <Text style={styles.proposalDetailLabel}>Parcelas</Text>
      <Text style={styles.proposalDetailValue}>
        {`${item?.proposta_valor_prazo}x`}
      </Text>
    </View>
    <View style={styles.lineContainer}>
      <Text style={styles.proposalDetailLabel}>Valor da parcela</Text>
      <Text style={styles.proposalDetailValue}>
        {formatCurrency(item?.proposta_valor_parcela)}
      </Text>
    </View>
    <View style={styles.buttonContainer}>
      <Button
        onPress={goToDetails}
        title="Ver detalhes"
        titleStyle={styles.proposalButtonText}
        style={styles.button}
      />
    </View>
  </>
</TouchableOpacity>

And the style of items

import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';

import { metrics, colors } from '../../../../constants';

const styles = StyleSheet.create({
container: (index, isLoweredCard) => ({
 backgroundColor: `#00${index}F${index}C`,
 marginTop: !isLoweredCard && index !== 0 ? -wp(53) : metrics.padding,
 marginHorizontal: metrics.padding,
 alignContent: 'center',
 padding: metrics.padding,
 borderRadius: metrics.radius,
 zIndex: -(index + 999),
}),
lineContainer: {
 width: '100%',
 justifyContent: 'space-between',
 flexDirection: 'row',
 marginBottom: metrics.padding / 2,
},
statusContainer: {
 backgroundColor: colors.white,
 borderRadius: 20,
 width: '70%',
 paddingVertical: 3,
 alignItems: 'center',
 justifyContent: 'center',
},
proposalId: {
 color: colors.white,
 fontWeight: 'bold',
 fontSize: wp(4.5),
},
proposalDate: {
 color: 'rgba(0, 0, 0, 0.5)',
 fontWeight: 'bold',
 fontSize: wp(3.5),
},
proposalDetailLabel: {
 fontSize: wp(4),
 color: 'rgba(0, 0, 0, 0.9)',
},
proposalDetailValue: {
 fontSize: wp(4.5),
 color: colors.white,
 fontWeight: 'bold',
},
proposalButtonText: {
 color: colors.white,
 fontWeight: 'bold',
 fontSize: wp(4),
},
button: {
 borderRadius: metrics.radius,
 backgroundColor: '#002F6C',
 paddingHorizontal: metrics.padding * 3,
},
buttonContainer: {
 width: '100%',
 marginTop: metrics.padding,
 alignItems: 'center',
},
});

export default styles;

Solution

  • I encountered exactly the same problem on Android. My FlatList rendered items with negative margin to superimpose them on each other. The touchable on iOS is perfect but on Android it press the lowest item behind.

    Finally I solved this by replacing

    import { TouchableOpacity } from 'react-native';
    

    with

    import { TouchableOpacity } from 'react-native-gesture-handler';
    

    Using react-native 0.63.4 and react-native-gesture-handler 1.9.0 in my case.