Having accurate information about your users’ location is a great way to enhance the user experience. For example, you could use this data to show users what is around them, offer exclusive deals for products and services in their area, and much more. Fortunately, if you have a React application, implementing maps is a breeze using the react-native-maps
library.
react-native-maps
is a component system for maps that ships with platform-native code that needs to be compiled together with React Native. In this guide, we’ll demonstrate how to integrate Google Maps into your React Native application and introduce you to fundamental components such as <MapView />
and <Marker>
.
Let’s get started!
The first thing you need to do is obtain the API key for configuring Google Maps on Android. Once you have the API key, include it in your AndroidManifest.xml
file as the child of the <application>
element.
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY"/>
Next, run the following command to install react-native-maps
in your project.
yarn add react-native-maps -E
Remove all the default code from the App.js
file and import the <MapView />
component from react-native-maps
.
import MapView from "react-native-maps";
Inside the component, render the <MapView />
component.
import MapView from "react-native-maps";
const App = () => {
return (
<MapView
style={{ flex: 1 }}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.05,
longitudeDelta: 0.05
}}
/>
);
};
export default App;
The initialRegion
prop shows the region that is to be displayed on the map when the component mounts. The initialRegion
value cannot be changed after it has been initialized. Don’t forget to add the style property to specify the dimensions. Otherwise, you’ll end up with a white screen. The value { flex: 1 }
will ensure that the <MapView />
takes up the entire screen.
You’ll need to pass an object with the latitude, longitude, and delta values of a region to the initialRegion
prop. The latitudeDelta
and longitudeDelta
properties specify how much the area on the map must be zoomed. To change the region, use the region
prop.
react-native-maps
with the useState
hookTo change the region with the state hook, use the onRegionChangeComplete
prop to set the new region into the state. The onRegionChangeComplete
is a callback prop that is called only once when the region changes.
import React, { useState } from "react";
import MapView from "react-native-maps";
const App = () => {
const [region, setRegion] = useState({
latitude: 51.5079145,
longitude: -0.0899163,
latitudeDelta: 0.01,
longitudeDelta: 0.01
});
return (
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={region => setRegion(region)}
/>
);
};
export default App;
Start by importing Marker
from react-native-maps
.
import MapView, { Marker } from "react-native-maps";
Next, render the <Marker />
component as a child of <MapView />
. Pass the coordinate for the marker in the coordinate
prop.
import React, { useState } from "react";
import MapView, { Marker } from "react-native-maps";
const App = () => {
const [region, setRegion] = useState({
latitude: 51.5078788,
longitude: -0.0877321,
latitudeDelta: 0.009,
longitudeDelta: 0.009
});
return (
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={region => setRegion(region)}
>
<Marker coordinate={{ latitude: 51.5078788, longitude: -0.0877321 }} />
</MapView>
);
};
export default App;
The marker should be visible, as shown below.
You can add n
number of markers to the map and pass it as a direct child to the <MapView />
component.
To change the color of the marker, use the pinColor
prop.
<Marker
coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}
pinColor="green"
/>
You can also add a custom marker image by passing the image
prop to the <Marker />
component.
<Marker
coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}
image={require("./car.png")}
/>
The following will display a location with a custom marker view component.
import React, { useState } from "react";
import { View, Text } from "react-native";
import MapView, { Marker } from "react-native-maps";
const CustomMarker = () => (
<View
style={{
paddingVertical: 10,
paddingHorizontal: 30,
backgroundColor: "#007bff",
borderColor: "#eee",
borderRadius: 5,
elevation: 10
}}
>
<Text style={{ color: "#fff" }}>Berlin</Text>
</View>
);
const App = () => {
const [region, setRegion] = useState({
latitude: 52.5200066,
longitude: 13.404954,
latitudeDelta: 0.005,
longitudeDelta: 0.005
});
return (
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={region => setRegion(region)}
>
<Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
<CustomMarker />
</Marker>
</MapView>
);
};
export default App;
Generate the JSON object you’ll use to design the map from the Google style generator.
Next, pass the generated style object to the customMapStyle
prop of the <MapView />
component.
import React, { useState } from "react";
import { View, Text } from "react-native";
import MapView, { Marker } from "react-native-maps";
const mapStyle = [
{
elementType: "geometry",
stylers: [
{
color: "#1d2c4d"
}
]
},
{
elementType: "labels.text.fill",
stylers: [
{
color: "#8ec3b9"
}
]
},
// ...
{
featureType: "water",
elementType: "geometry.fill",
stylers: [
{
color: "#3e73fd"
}
]
},
{
featureType: "water",
elementType: "labels.text.fill",
stylers: [
{
color: "#4e6d70"
}
]
}
];
const CustomMarker = () => (
<View
style={{
paddingVertical: 10,
paddingHorizontal: 30,
backgroundColor: "#fff",
borderColor: "#eee",
borderRadius: 5,
elevation: 10
}}
>
<Text>Berlin</Text>
</View>
);
const App = () => {
const [region, setRegion] = useState({
latitude: 52.5200066,
longitude: 13.404954,
latitudeDelta: 0.005,
longitudeDelta: 0.005
});
return (
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={region => setRegion(region)}
customMapStyle={mapStyle}
>
<Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
<CustomMarker />
</Marker>
</MapView>
);
};
export default App;
You can ignore the mapStyle
variable. Since it will be generated from the map style generator, you only need to paste the JSON object into your code and send it to the <MapView />
component.
As you can see, the map style changed from the default light theme to a customized dark theme.
What if you want to animate to a particular coordinate?
First, create a ref to <MapView />
using the useRef()
hook.
import React, { useState, useRef, useEffect } from "react";
import { View, Text } from "react-native";
import MapView, { Marker } from "react-native-maps";
// ...
const App = () => {
const _map = useRef(null);
useEffect(() => {
// ...
}, []);
return (
<>
<MapView
style={{ flex: 1 }}
ref={_map}
initialRegion={{
latitude: 52.5200066,
longitude: 13.404954,
latitudeDelta: 0.1,
longitudeDelta: 0.1
}}
>
<Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
<CustomMarker />
</Marker>
</MapView>
</>
);
};
export default App;
Next, inside the useEffect()
hook, use the animateCamera()
function to animate the MapView
region.
useEffect(() => {
if(_map.current) {
_map.current.animateCamera(
{
center: {
latitude: 50.1109221,
longitude: 8.6821267
}
zoom: 15
},
5000
);
}
}, []);
The useRef()
hook returns a mutable ref object whose current
property has the value of the passed argument. If the value of current
property is undefined
, that means the component is not yet mounted. Now you can access any of the <MapView />
methods using _map.current
.
The animateCamera()
method accepts an object with center
, zoom
, heading
, and altitude
properties as its argument and the animation duration in milliseconds. You can pass the region’s latitude and longitude in the center
property, but unlike the initialRegion
prop, you cannot give the delta values. The zoom
property specifies the extent to which the region needs to be zoomed.
You can use the <Polyline />
component from the react-native-maps
library to create lines between multiple coordinates. It accepts an array of coordinates in its coordinates
prop. You can also specify additional props for styling purposes, such as strokeWidth
, strokeColor
, etc.
Let’s create a path between Berlin and Frankfurt.
import React from "react";
import MapView, { Polyline } from "react-native-maps";
const App = () => {
const Berlin = {
latitude: 52.5200066,
longitude: 13.404954
};
const Frankfurt = {
latitude: 50.1109221,
longitude: 8.6821267
};
return (
<>
<MapView
style={{ flex: 1 }}
initialRegion={{
latitude: 52.5200066,
longitude: 13.404954,
latitudeDelta: 0.1,
longitudeDelta: 0.1
}}
>
<Polyline coordinates={[Berlin, Franfurt]} />
</MapView>
</>
);
};
export default App;
Store the coordinates for the locations in their respective variables and pass them in an array to the coordinates
prop of the <Polyline />
component.
If you look at the results, the line is drawn directly between these coordinates and does not take into account the actual geographical paths and roads. For that, you’ll need to establish multiple coordinates between the source and destination locations by using the Google Maps Direction API, which returns all possible routes between two places.
Create a getDirections
function that accepts the coordinates in string format.
const getDirections = async (startLoc, destinationLoc) => {
try {
const KEY = "YOUR GOOGLE API KEY";
let resp = await fetch(
`https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${destinationLoc}&key=${KEY}`
);
let respJson = await resp.json();
let points = decode(respJson.routes[0].overview_polyline.points);
console.log(points);
let coords = points.map((point, index) => {
return {
latitude: point[0],
longitude: point[1]
};
});
return coords;
} catch (error) {
return error;
}
};
Import the decode()
function from the @mapbox/polyline
library.
import { decode } from "@mapbox/polyline";
The decode()
function will convert the encoded polyline points from the overview_polyline.points
property into an array containing the latitude and longitude of all the coordinates.
import React, { useState, useEffect } from "react";
import { View, Text } from "react-native";
import MapView, { Polyline } from "react-native-maps";
import { decode } from "@mapbox/polyline";
const getDirections = async (startLoc, destinationLoc) => {
try {
const KEY = "YOUR GOOGLE API KEY";
let resp = await fetch(
`https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${destinationLoc}&key=${KEY}`
);
let respJson = await resp.json();
let points = decode(respJson.routes[0].overview_polyline.points);
let coords = points.map((point, index) => {
return {
latitude: point[0],
longitude: point[1]
};
});
return coords;
} catch (error) {
return error;
}
};
const App = () => {
const [coords, setCoords] = useState([]);
useEffect(() => {
getDirections("52.5200066,13.404954", "50.1109221,8.6821267")
.then(coords => setCoords(coords))
.catch(err => console.log("Something went wrong"));
}, []);
return (
<>
<MapView
style={{ flex: 1 }}
initialRegion={{
latitude: 52.5200066,
longitude: 13.404954,
latitudeDelta: 0.1,
longitudeDelta: 0.1
}}
>
{coords.length > 0 && <Polyline coordinates={coords} />}
</MapView>
</>
);
};
export default App;
You should now have a basic understanding of how to implement maps, their benefits, and how to build features on top of the react-native-maps
library. You can implement these lessons into your work to build myriad types of applications, from taxi services, to restaurant finders, to delivery apps, and much more. The react-native-maps
library makes it simple to integrate maps and is an essential player in the React Native ecosystem.
Originally published by Gaurav Singhal at https://blog.logrocket.com
React Native Maps Tutorial
How to Use Google Maps in React Native - Part 1
How to Use Google Maps in React Native - Part 2
How to Use Google Maps in React Native - Part 3
#react-native #reactjs #mobile-apps