Practice and reinforce the concepts from Lesson 1
Create a fully functional interactive map that displays the user's current location with custom styling and basic controls. You'll learn to integrate Google Maps SDK into an Expo app and handle location permissions.
An interactive map application that shows your current location, allows zooming and panning, and includes custom map styling for a unique look.

Open your starter template and install the required dependencies.
cd M3-Activity-01
npm install react-native-maps expo-location
Create your main map component:
// App.js
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Alert } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import * as Location from 'expo-location';
export default function App() {
const [location, setLocation] = useState(null);
const [region, setRegion] = useState({
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
});
// We'll add location logic here
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={region}
showsUserLocation={true}
showsMyLocationButton={true}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
width: '100%',
height: '100%',
},
});
Add the permission request logic to access the user's location.
useEffect(() => {
(async () => {
// Request foreground location permission
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert(
'Permission Denied',
'Location permission is needed to show your position on the map'
);
return;
}
// Get current location
let currentLocation = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High
});
setLocation(currentLocation);
// Update map region to user's location
setRegion({
latitude: currentLocation.coords.latitude,
longitude: currentLocation.coords.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
})();
}, []);
💡 Tip: Always request permissions before accessing location services. On iOS, you'll also need to add location usage descriptions in app.json.
Create a custom map style to make your map unique. Add this styling configuration:
const customMapStyle = [
{
"featureType": "water",
"elementType": "geometry",
"stylers": [{ "color": "#193341" }]
},
{
"featureType": "landscape",
"elementType": "geometry",
"stylers": [{ "color": "#2c5a71" }]
},
{
"featureType": "road",
"elementType": "geometry",
"stylers": [
{ "color": "#29768a" },
{ "lightness": -37 }
]
},
{
"featureType": "poi",
"elementType": "geometry",
"stylers": [{ "color": "#406d80" }]
},
{
"featureType": "transit",
"elementType": "geometry",
"stylers": [{ "color": "#406d80" }]
},
{
"elementType": "labels.text.stroke",
"stylers": [
{ "visibility": "on" },
{ "color": "#3e606f" },
{ "weight": 2 },
{ "gamma": 0.84 }
]
},
{
"elementType": "labels.text.fill",
"stylers": [{ "color": "#ffffff" }]
}
];
Apply the style to your MapView:
<MapView
style={styles.map}
region={region}
showsUserLocation={true}
showsMyLocationButton={true}
customMapStyle={customMapStyle}
/>
Implement zoom controls and map type switcher:
import { StyleSheet, View, Alert, TouchableOpacity, Text } from 'react-native';
const [mapType, setMapType] = useState('standard');
const zoomIn = () => {
setRegion({
...region,
latitudeDelta: region.latitudeDelta / 2,
longitudeDelta: region.longitudeDelta / 2,
});
};
const zoomOut = () => {
setRegion({
...region,
latitudeDelta: region.latitudeDelta * 2,
longitudeDelta: region.longitudeDelta * 2,
});
};
const toggleMapType = () => {
setMapType(mapType === 'standard' ? 'satellite' : 'standard');
};
// Add to your return statement
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={region}
mapType={mapType}
showsUserLocation={true}
showsMyLocationButton={true}
customMapStyle={mapType === 'standard' ? customMapStyle : null}
/>
<View style={styles.controls}>
<TouchableOpacity style={styles.button} onPress={zoomIn}>
<Text style={styles.buttonText}>+</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={zoomOut}>
<Text style={styles.buttonText}>-</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={toggleMapType}>
<Text style={styles.buttonText}>🗺️</Text>
</TouchableOpacity>
</View>
</View>
);
// Add to styles
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
width: '100%',
height: '100%',
},
controls: {
position: 'absolute',
right: 20,
top: 60,
gap: 10,
},
button: {
backgroundColor: 'white',
width: 50,
height: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
buttonText: {
fontSize: 24,
fontWeight: 'bold',
},
});
Add necessary permissions and API key to your app.json:
{
"expo": {
"name": "Interactive Map",
"slug": "interactive-map",
"version": "1.0.0",
"orientation": "portrait",
"ios": {
"supportsTablet": true,
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "This app needs your location to show you on the map."
},
"config": {
"googleMapsApiKey": "YOUR_IOS_API_KEY"
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"permissions": [
"ACCESS_COARSE_LOCATION",
"ACCESS_FINE_LOCATION"
],
"config": {
"googleMaps": {
"apiKey": "YOUR_ANDROID_API_KEY"
}
}
}
}
}
Problem: Map shows blank gray screen Solution: Check that your API key is valid and has Maps SDK for Android/iOS enabled in Google Cloud Console. Ensure you're using the correct key for each platform.
Problem: Location permission dialog doesn't appear Solution: Delete the app from your phone and reinstall. Permissions are cached once denied. Also verify app.json has the correct permission entries.
Problem: Map doesn't center on user location Solution: Ensure you're testing on a physical device (not simulator) or enable location simulation in your simulator settings.
Problem: Custom map style not showing Solution: Custom styles only work with standard map type. When switching to satellite view, styles are automatically disabled.
Problem: Controls not visible on the map Solution: Check the z-index and ensure controls have position: 'absolute'. Also verify the top/right positioning values don't push buttons off-screen.
For advanced students:
Location Tracking: Add continuous location tracking that updates the map as you move (use Location.watchPositionAsync)
Multiple Map Styles: Create 3-4 different map style themes (dark mode, retro, minimal) and let users choose from a style picker
Distance Measurement: Add a feature that lets users tap two points on the map and displays the distance between them
Compass Integration: Show the user's heading direction and rotate the map based on device orientation
Location History: Track and display a path of where the user has been during the session using a Polyline
In this activity, you:
In the next lesson, you'll explore custom markers and overlays to add rich visual information to your maps. You'll learn how to create custom marker designs, cluster markers for better performance, and add shapes like circles and polygons to highlight areas of interest.