How to make react-native-google-places-autocomplete currentLocation work in Expo app

652 Views Asked by At

Everything worked fine when currentLocation = {false}. The trouble starts as soon as you make it = {true}. Boy oh boy, first thing you get this warning :

If you are using React Native v0.60.0+ you must follow these instructions to enable currentLocation: https://git.io/Jf4AR

Indeed, if you consult the documentation for react-native-google-places-autocomplete, it says this: "If you are targeting React Native 0.60.0+ you must install either @react-native-community/geolocation (https://github.com/react-native-community/react-native-geolocation) or react-native-geolocation-service(https://github.com/Agontuk/react-native-geolocation-service).

Please make sure you follow the installation instructions there and add navigator.geolocation = require(GEOLOCATION_PACKAGE) somewhere in you application before ."

I then tried to install those packages and added the line navigator.geolocation = require(GEOLOCATION_PACKAGE) to my code.

The problem is that those two packages need pod installs something I can't really do in my expo app.

I then tried to replace those packages with 'expo-location' which basically does the same thing. The problem is that it does have a getCurrentPosition() function. Instead, it's called getCurrentPositionAsync() and it works slightly differently. When running the code with navigator.geolocation = require('expo-location'), I get this error:

TypeError: Cannot read property 'getCurrentPosition' of undefined

Can someone help me out?

Here's my code:

import { CommonActions, useNavigation } from "@react-navigation/native";
import React, { useContext, useEffect, useRef, useState } from "react";
import {
  Alert,
  Image,
  Keyboard,
  Linking,
  Modal,
  Platform,
  Pressable,
  ScrollView,
  StyleSheet,
  Text,
  View,
  useWindowDimensions,
} from "react-native";

import { Camera } from "expo-camera";
import * as MediaLibrary from "expo-media-library";
import * as ImagePicker from "expo-image-picker";
import AsyncStorage from "@react-native-async-storage/async-storage";

import BottomSheet from "@gorhom/bottom-sheet";
import { GestureHandlerRootView } from "react-native-gesture-handler";

import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplete";

import { COLORS } from "../../assets/Colors/Colors";

import { Icon } from "@rneui/themed";

import CustomButton from "../../components/CustomButton";
import CustomInput from "../../components/CustomInput";
import SocialSignInButtons from "../../components/SocialSignInButtons";
import loginContext from "../../components/loginContext/loginContext";

navigator.geolocation = require("expo-location");

const ProfileCreationPage = ({ route }) => {
  const [userBio, setUserBio] = useState("");
  const [selectedPlaces, setSelectedPlaces] = useState([]);
  const [hasGalleryPermission, setHasGalleryPermission] = useState(null);
  const [hasCameraPermission, setHasCameraPermission] = useState(null);
  const [keyboardHeight, setKeyboardHeight] = useState(0);
  const [image, setImage] = useState(null);
  const [isBottomSheetOpened, setIsBottomSheetOpened] = useState(false);

  const { status, login, hasForegroundPermissions, userLocation, userHeading } =
    useContext(loginContext);

  const window = useWindowDimensions();
  const navigation = useNavigation();
  const textInput1 = useRef();
  const bottomSheetRef = useRef();

  useEffect(() => {
    setTimeout(() => {
      textInput1.current?.focus();
    }, 0);
    const keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      (e) => {
        setKeyboardHeight(e.endCoordinates.height);
      }
    );

    const keyboardDidHideListener = Keyboard.addListener(
      "keyboardDidHide",
      () => {
        setKeyboardHeight(0);
      }
    );

    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);

  useEffect(() => {
    async function checkImage() {
      try {
        const Value = await AsyncStorage.getItem("@Wassupp_user");
        const User = JSON.parse(Value);
        const Image = User.Image;
        const Bio = User.Bio;
        if (Image !== undefined) {
          setImage(Image);
        }
        if (Bio !== undefined) setUserBio(Bio);
        console.log(JSON.stringify(User));
      } catch (e) {
        console.warn(e);
      }
    }
    checkImage();
  }, []);

  const onNextPressed = async () => {
    // validate user
    try {
      const value = await AsyncStorage.getItem("@Wassupp_user");
      const User = JSON.parse(value);
      User.Bio = userBio;
      await AsyncStorage.setItem("@Wassupp_user", JSON.stringify(User));
    } catch (e) {
      console.warn(e);
    }
    console.log("Confirm pressed");
    console.log(await AsyncStorage.getItem("@Wassupp_user"));
    if (route.params)
      navigation.navigate(route.params.previousScreen, {
        screen: route.params.previousNestedScreen,
      });
    else navigation.reset({ index: 0, routes: [{ name: "Home" }] });
  };

  const pickImageGal = async () => {
    function appSettings() {
      console.warn("Open settigs pressed");
      if (Platform.OS === "ios") {
        Linking.openURL("app-settings:");
      } else RNAndroidOpenSettings.appDetailsSettings();
    }

    const appSettingsALert = () => {
      Alert.alert(
        "Allow Wassupp to Access your Photos",
        "Open your app settings to allow Wassupp to access your camera roll.",
        [
          {
            text: "Cancel",
            onPress: () => console.warn("Cancel pressed"),
          },
          { text: "Open settings", onPress: appSettings },
        ]
      );
    };
    const galleryPermission = await MediaLibrary.requestPermissionsAsync();
    if (
      galleryPermission.canAskAgain == false ||
      galleryPermission.status == "denied"
    ) {
      appSettingsALert();
    }
    setHasGalleryPermission(galleryPermission.status === "granted");
    if (hasGalleryPermission == true) {
      try {
        let result = await ImagePicker.launchImageLibraryAsync({
          mediaTypes: ImagePicker.MediaTypeOptions.All,
          allowsEditing: true,
          aspect: [1, 1],
          quality: 1,
        });

        console.log(result);

        if (!result.canceled) {
          setImage(result.assets[0].uri);
          const ImageResults = { Image: result.assets[0].uri };
          try {
            const value = await AsyncStorage.getItem("@Wassupp_user");
            const User = JSON.parse(value);
            if (User.Image == undefined) {
              await AsyncStorage.mergeItem(
                "@Wassupp_user",
                JSON.stringify(ImageResults)
              );
            } else {
              User.Image = result.assets[0].uri;
              await AsyncStorage.setItem("@Wassupp_user", JSON.stringify(User));
            }
            console.log(JSON.stringify(User));
          } catch (e) {
            console.warn(e);
          }
        }
      } catch (error) {
        console.error(error);
      }
    }
  };

  const snapBottomSheetToIndex = (number) => {
    bottomSheetRef.current.snapToIndex(number);
  };

  const onAddLocationPressed = () => {
    snapBottomSheetToIndex(0);
    setIsBottomSheetOpened(true);
  };

  const styles = StyleSheet.create({
    container: {
      backgroundColor: COLORS.background_Pale,
      flex: 1,
      //bottom: keyboardHeight,
    },
    background: {
      backgroundColor: COLORS.background_Pale,
      flex: 1,
      bottom: isBottomSheetOpened ? 0 : keyboardHeight,
    },
    imageSlider: {
      alignContent: "center",
      aspectRatio: 1 / 1,
      backgroundColor: COLORS.image_Slider_Background,
      borderColor: COLORS.input_Border_Violet,
      borderWidth: 7.5,
      justifyContent: "center",
      width: "100%",
    },
    title: {
      color: COLORS.background,
      fontSize: 20,
      fontWeight: "bold",
      marginTop: 20,
      marginLeft: 20,
    },
  });

  return (
    <GestureHandlerRootView style={{ flex: 1, backgroundColor: "black" }}>
      <ScrollView style={styles.container}>
        <View style={styles.background}>
          <Pressable
            style={[
              styles.imageSlider,
              image === null ? {} : { borderWidth: 0 },
            ]}
            onPress={pickImageGal}
          >
            {image == null ? (
              <View>
                <Icon
                  name="images"
                  type="font-awesome-5"
                  color={COLORS.background}
                  size={40}
                />
                <Text
                  style={{
                    textAlign: "center",
                    color: COLORS.background,
                    fontSize: 16,
                    fontWeight: "bold",
                  }}
                >
                  Add images to your profile
                </Text>
              </View>
            ) : (
              <View style={{ flex: 1 }}>
                <Image source={{ uri: image }} style={{ flex: 1 }} />
              </View>
            )}
          </Pressable>
          <Text style={styles.title}>About me</Text>
          <CustomInput
            ref={textInput1}
            multiline={true}
            textAlign={"justify"}
            maxLength={500}
            style={{ marginLeft: 10, width: window.width * 1.184 }}
            value={userBio}
            onChangeText={(textValue) => {
              setUserBio(textValue);
            }}
          />
          <Text style={styles.title}>Places to take me on a date</Text>
          <CustomButton
            text="Add location"
            onPress={() => {
              onAddLocationPressed();
            }}
            style={{ alignSelf: "center", marginTop: 15 }}
            type={selectedPlaces.length == 1 ? "SECONDARY" : "SECONDARY2"}
            image={require("/Users/aurelie/Wassupp/app/assets/AppIcon/App_Icon.png")}
          />
          <CustomButton
            onPress={() => {
              onNextPressed(route);
            }}
            style={{ alignSelf: "center", marginTop: 30, width: "30%" }}
            text="Confirm"
            type="PRIMARY_SOCIAL_MEDIA"
          />
        </View>
      </ScrollView>
      <BottomSheet
        index={-1}
        ref={bottomSheetRef}
        onClose={() => {
          setIsBottomSheetOpened(false);
        }}
        snapPoints={[keyboardHeight != 0 ? 200 + keyboardHeight : 200]}
        enablePanDownToClose={true}
      >
        <View style={{ flex: 1 }}>
          <GooglePlacesAutocomplete
            placeholder="Search"
            currentLocation={true}
            minLength={2}
            debounce={200}
            nearbyPlacesAPI="GooglePlacesSearch"
            onPress={(data, details = null) => {
              // 'details' is provided when fetchDetails = true
              console.log(data, details);
            }}
            query={{
              key: "YOUR GOOGLE API KEY",
              language: "en",
            }}
          />
        </View>
      </BottomSheet>
    </GestureHandlerRootView>
  );
};
export default ProfileCreationPage;

Please save me!

1

There are 1 best solutions below

1
On

Try it like this:

  1. Change import statement to this
import * as Location from 'expo-location';
  1. Create State to store current location
const [location, setLocation] = useState(null);
  1. Check for permission and get Current Location
  useEffect(() => {
    (async () => {
      
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }

      let location = await Location.getCurrentPositionAsync({});
      setLocation(location);
    })();
  }, []);