react-native-google-places-autocomplete icon indicating copy to clipboard operation
react-native-google-places-autocomplete copied to clipboard

Example to resolve "VirtualizedLists should never be nested inside plain..."

Open mananjadhav opened this issue 3 years ago • 2 comments

If you add RNGooglePlacesAutocomplete inside a ScrollView, it throws an error:

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation - use another VirtualizedList-backed container instead.

This PR adds an example in the README to fix this by wrapping another ScrollView.

This fix is being currently added in this PR https://github.com/Expensify/App/issues/7547 to get the component working inside a ScrollView

mananjadhav avatar Mar 21 '22 15:03 mananjadhav

This removes the error but now the field dynamically adjusts the width based on the contents. How do I keep a fixed input field width?

hb-webdev avatar Jun 01 '22 17:06 hb-webdev

This removes the error but now the field dynamically adjusts the width based on the contents. How do I keep a fixed input field width?

container style in scrollview should fix it

import React, { useState, useRef,useEffect } from 'react';
import { View, Text, TouchableOpacity, FlatList, Modal, StyleSheet, Dimensions,ScrollView } from 'react-native';
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
import { GOOGLE_MAPS_AUTOCOMPLETE_API_KEY } from '../../constants';

const { width, height } = Dimensions.get('window');

const TennisCourtSearch = ({ onSelect, latitude, longitude }) => {
  const searchRef = useRef();
  const [selectedCourts, setSelectedCourts] = useState([]);
  const [showModal, setShowModal] = useState(false);
  useEffect(() => {
    searchRef.current?.setAddressText('Search for tennis courts');
  }, []);


  useEffect(() => {
    onSelect(selectedCourts); // Notify parent component whenever selectedCourts changes
  }, [selectedCourts]);

  const handleSelectCourt = (detail) => {
    const newCourt = {
      formattedAddress: detail.formatted_address,
      geometry: detail.geometry
    };
    const index = selectedCourts.findIndex(court => court.geometry.location === newCourt.geometry.location);
    if (index === -1) {
      setSelectedCourts(prevCourts => [...prevCourts, newCourt]);
    } else {
      setSelectedCourts(prevCourts => prevCourts.filter(court => court.geometry.location !== newCourt.geometry.location));
    }
  };

  const renderCourtItem = ({ item, index }) => (
    <View style={styles.courtItem}>
      <Text style={styles.courtText} ellipsizeMode='tail'  numberOfLines={1}>{`${index + 1}. ${item.formattedAddress}`}</Text>
     <TouchableOpacity onPress={() => handleSelectCourt(item)} style={styles.removeButton}>
        <Text style={styles.removeButtonText}>Remove</Text>
      </TouchableOpacity>
    </View>
  );

  console.log("selectedCourts",JSON.stringify(selectedCourts));
  return (
    <View style={{ marginTop: 50 }} keyboardShouldPersistTaps="handled">
        <ScrollView
    horizontal
    style={{ flex: 1, width: '100%', height: '100%' }}
    contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center' }}
    scrollEnabled={false}
        >
      <GooglePlacesAutocomplete
        ref={searchRef}
        placeholder="Search for tennis courts"
        minLength={2}
        fetchDetails={true}

        disableScroll={true}
        onPress={(data, details = null) => {
          if (data && details) {
            handleSelectCourt(details);
          }
        }}
        query={{
          key: GOOGLE_MAPS_AUTOCOMPLETE_API_KEY,
          language: 'en',
          location: `${latitude},${longitude}`,
          radius: 10000,
          type: 'establishment',
          keyword: 'tennis court',
        }}

        // disableScroll={true}
        nearbyPlacesAPI="GooglePlacesSearch"
        debounce={300}
        
        suppressDefaultStyles={true}
                  styles={{
                    container: {
                      flex: 1,
                    },
                    textInputContainer: {
                      flexDirection: 'row',
                    },
                    textInput: {
                      color: '#5d5d5d',
                      backgroundColor: '#FFFFFF',
                      height: 44,
                      borderRadius: 5,
                      paddingVertical: 5,
                      paddingHorizontal: 10,
                      fontSize: 15,
                      flex: 1,
                      marginBottom: 5,
                    },
                    listView: {color : 'black'},
                    row: {
                      backgroundColor: '#FFFFFF',
                      padding: 13,
                      minHeight: 44,
                      flexDirection: 'row',
                    },
                    loader: {
                      flexDirection: 'row',
                      justifyContent: 'flex-end',
                      height: 20,
                    },
                    description : {color : 'black'},
                    separator: {
                      height: StyleSheet.hairlineWidth,
                      backgroundColor: '#c8c7cc',
                    },
                    poweredContainer: {
                      justifyContent: 'flex-end',
                      alignItems: 'center',
                      borderBottomRightRadius: 5,
                      borderBottomLeftRadius: 5,
                      borderColor: '#c8c7cc',
                      borderTopWidth: 0.5,
                    },
                  }}
        enablePoweredByContainer={false}
      />
      </ScrollView>
     
      <TouchableOpacity onPress={() => setShowModal(true)} style={styles.button}>
        <Text>Show Selected Courts ({selectedCourts.length})</Text>
      </TouchableOpacity>
      <Modal
        visible={showModal}
        animationType="slide"
        onRequestClose={() => setShowModal(false)}
      >
        <View style={styles.modalView}>
          <FlatList
            data={selectedCourts}
            renderItem={renderCourtItem}
            keyExtractor={item => item.place_id}
            ListEmptyComponent={<Text>No courts selected.</Text>}
          />
          <TouchableOpacity onPress={() => setShowModal(false)} style={styles.button}>
            <Text>Close</Text>
          </TouchableOpacity>
        </View>
      </Modal>
    </View>
  );
};

const styles = StyleSheet.create({
  button: {
    marginTop: 10,
    backgroundColor: 'lightgrey',
    padding: 10,
    alignItems: 'center',
  },
  courtItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 10,
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
  },
  courtText: {
    color:'black',
    fontSize: 16,
      width:200,
  },
  removeButton: {
    padding: 5,
    backgroundColor: 'red',
    borderRadius: 5,
  },
  removeButtonText: {
    color: 'white',
  },
  modalView: {
    width: width * 0.9,
    height: height * 0.7,
    alignSelf: 'center',
    marginTop: height * 0.15,
    backgroundColor: 'white',
    borderRadius: 20,
    padding: 20,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
  },
});

export default TennisCourtSearch;

sahilkashyap64 avatar Sep 05 '24 22:09 sahilkashyap64