En la actualidad, añadir mapas a aplicaciones móviles es un requisito esencial debido a funcionalidades como tracking, navegación o mostrar ubicaciones de usuarios. En Juice Studio, desarrollamos aplicaciones con React Native que están diseñadas para ayudarte a construir productos capaces de obtener la ubicación de manera eficiente.
En este post, te mostraremos cómo implementar Google Maps en React Native utilizando react-native-maps en un proyecto React Native. Te guiaremos a través del setup necesario, configuraciones y permisos, y mostraremos una vista de mapa. Al completar este tutorial, estarás preparado para cualquier aplicación que desees construir, ya sea una simple pantalla de mapa o funcionalidades más complejas.
Prerrequisitos para implementar Google Maps en React Native
Verifica que tengas todas las herramientas de React Native configuradas en tu sistema. Si aún no está configurada, puedes completar la guía oficial de configuración de React Native.
Qué aprenderás en este tutorial para implementar Google Maps en React Native
En esta guía, recorreremos los aspectos clave de trabajar con mapas en un proyecto React Native CLI, incluyendo:
- Introducción a React Native Maps con la librería react-native-maps
- Instalación y configuración completa paso a paso
- Solicitar y manejar permisos de ubicación
- Crear una pantalla de ejemplo con vista de mapa
- Mostrar la ubicación actual del usuario en el mapa
- Dibujar una polilínea entre dos coordenadas
- Usar Google Maps Directions API para características avanzadas
Introducción a React Native Maps para implementar Google Maps en React Native
React-native-maps es una librería popular para integrar mapas en aplicaciones Android e iOS desarrolladas usando React Native. Tiene componentes principales como MapView y Marker. Estos te ayudan a mostrar mapas, dar pins y manejar interacciones de usuario como toques y gestos.
Características clave al implementar Google Maps en React Native:
- Utiliza servicios de mapas nativos para rendimiento óptimo
- Funciona con Google Maps en Android e iOS
- Compatibilidad con Apple Maps en iOS como alternativa
- Amplia personalización: tipos de mapas, controles de zoom, overlays y marcadores
- Funcionalidades avanzadas: clustering, mapas offline, estilos personalizados
Para empezar a implementar Google Maps en React Native, instala los siguientes paquetes:
npm install react-native-maps
npm install react-native-geolocation-service
npm install react-native-permissions
Qué hace cada paquete para implementar Google Maps en React Native:
- react-native-maps: Se usa para mostrar y controlar la vista del mapa
- react-native-geolocation-service: Ofrece la ubicación del dispositivo con mayor precisión
- react-native-permissions: Se usa para solicitar y verificar permisos de ubicación para Android e iOS
Configuración de React Native Maps para iOS
Paso 1: Configurar Podfile
Añade la siguiente dependencia a tu Podfile ubicado en el directorio ios:
pod 'react-native-maps', :path => '../node_modules/react-native-maps', :subspecs => ['Google']
Paso 2: Actualizar script de permisos
Actualiza tu Podfile con el siguiente script para manejar la configuración de permisos correctamente:
def node_require(script)
# Resolve script with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
"require.resolve(
'#{script}',
{paths: [process.argv[1]]},
)", __dir__]).strip
end
node_require('react-native/scripts/react_native_pods.rb')
node_require('react-native-permissions/scripts/setup.rb')
setup_permissions([
'LocationAccuracy',
'LocationAlways',
'LocationWhenInUse',
])
Paso 3: Instalar pods
Ejecuta el comando pod install para instalar los pods:
cd ios && pod install
Configuración de React Native Maps para Android
Paso 1: Configurar build.gradle de la app
Añade la siguiente dependencia a tu archivo android/app/build.gradle
dentro del bloque dependencies:
implementation ("com.google.android.gms:play-services-location:21.1.0")
Paso 2: Configurar build.gradle del proyecto
Añade la siguiente configuración a tu archivo android/build.gradle
fuera del bloque buildscript (a nivel raíz):
allprojects {
configurations.all {
resolutionStrategy {
force 'com.google.android.gms:play-services-location:21.1.0'
}
}
}
Esto asegura que se use consistentemente la misma versión de play-services-location en todas las librerías, evitando crashes en tiempo de ejecución causados por desajustes de versión.
Solicitar y manejar permisos de ubicación al implementar Google Maps en React Native
iOS – Añade las siguientes descripciones de permisos en Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Esta app requiere acceso a tu ubicación para mostrar tu posición actual en el mapa</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Esta app requiere acceso a tu ubicación para mostrar tu posición actual en el mapa</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Esta app requiere acceso a tu ubicación para mostrar tu posición actual en el mapa</string>
Android – Añade los siguientes permisos en AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Crear utility para manejar permisos
Vamos a crear un archivo utility PermissionHandler.ts
para manejar toda la lógica relacionada con permisos:
import { PermissionsAndroid, Platform } from "react-native";
import { PERMISSIONS, request, RESULTS } from "react-native-permissions";
export const requestLocationPermission = async (): Promise<boolean> => {
try {
if (Platform.OS === "android") {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
);
return granted == PermissionsAndroid.RESULTS.GRANTED;
} else {
const result = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
console.log(result);
return (result == RESULTS.GRANTED || result == RESULTS.LIMITED);
}
} catch (error) {
console.warn("Permission error:", error);
return false;
}
};
Crear pantalla de ejemplo con vista de mapa
Vamos a crear una pantalla llamada HomeScreen
. Para mantener todas las pantallas organizadas, comienza creando una carpeta src
. Dentro de la carpeta src
, crea una carpeta llamada screens
, y dentro de esa, un archivo llamado HomeScreen.tsx
.
Código básico para implementar Google Maps en React Native:
import React from 'react';
import { View } from 'react-native';
import MapView from 'react-native-maps';
const HomeScreen = () => {
return (
<View style={{ flex: 1 }}>
<MapView
style={{ flex: 1 }}
provider="google"
initialRegion={{
latitude: 40.4168,
longitude: -3.7038,
latitudeDelta: 0.001,
longitudeDelta: 0.001,
}}
/>
</View>
);
};
export default HomeScreen;
Explicación del código para implementar Google Maps en React Native:
- style={{ flex: 1 }}: Se aplica tanto al View como al MapView para que el mapa cubra todo el espacio de pantalla disponible
- provider=»google»: Especifica que Google Maps debe usarse como proveedor del mapa
- initialRegion: Establece la posición inicial del mapa y nivel de zoom con valores de latitud, longitud y delta
Configurar API Key de Google Maps
iOS (en AppDelegate.swift):
import GoogleMaps
GMSServices.provideAPIKey("YOUR_GOOGLE_MAPS_API_KEY")
Android (en AndroidManifest.xml):
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_GOOGLE_MAPS_API_KEY"/>
Mostrar la ubicación actual del usuario al implementar Google Maps en React Native
Para mostrar la ubicación actual del usuario en el mapa, primero necesitamos obtenerla. Esto involucra solicitar permiso del usuario para acceder a su ubicación.
Implementación completa con geolocalización:
import { useEffect, useRef, useState } from "react";
import { View, Alert } from "react-native";
import MapView, { Marker, Region } from 'react-native-maps';
import Geolocation from 'react-native-geolocation-service';
import type MapViewType from 'react-native-maps';
import { requestLocationPermission } from "../../utils/PermissionHandler";
type Location = {
latitude: number;
longitude: number;
};
const HomeScreen = () => {
const [location, setLocation] = useState<Location | null>(null);
const [region, setRegion] = useState<Region>({
latitude: 0,
longitude: 0,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
const mapRef = useRef<MapViewType | null>(null);
useEffect(() => {
const getLocation = async () => {
const hasPermission = await requestLocationPermission();
if (!hasPermission) {
Alert.alert('Permiso Denegado', 'Se requiere acceso a la ubicación.');
return null;
}
Geolocation.getCurrentPosition(position => {
const { latitude, longitude } = position.coords;
const userLocation = { latitude, longitude };
setLocation(userLocation);
const newRegion = {
...userLocation,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
setRegion(newRegion);
if (mapRef.current) {
mapRef.current.animateToRegion(newRegion, 1000);
}
}, error => {
Alert.alert('Error', error.message);
}, {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
});
};
getLocation();
}, []);
return (
<View style={{ flex: 1 }}>
<MapView
ref={mapRef}
style={{ flex: 1 }}
provider="google"
region={region}
>
{location && (
<Marker
coordinate={location}
title="Estás aquí"
description="Ubicación actual"
/>
)}
</MapView>
</View>
);
};
export default HomeScreen;
Dibujar polilínea entre dos coordenadas
Para conectar la ubicación del usuario con un destino, vamos a implementar Google Maps en React Native con Polyline:
import { Polyline } from 'react-native-maps';
// Añadir destino
const destination = {
latitude: 40.4178,
longitude: -3.7040,
};
// Reemplazar el código animateToRegion existente:
if (mapRef.current) {
mapRef.current.fitToCoordinates(
[userLocation, destination],
{
edgePadding: { top: 80, right: 80, bottom: 80, left: 80 },
animated: true,
}
);
}
// Dentro del MapView, añadir:
<Marker coordinate={destination} title="Destino" />
<Polyline
coordinates={[location, destination]}
strokeColor="#1E90FF"
strokeWidth={4}
/>
Implementar Google Maps Directions API para características avanzadas
Para mostrar rutas reales utilizando Google Directions API al implementar Google Maps en React Native:
type LatLng = {
latitude: number;
longitude: number;
};
const fetchRouteCoordinates = async (
source: LatLng,
destination: LatLng
) => {
try {
const API_KEY = 'YOUR_GOOGLE_MAPS_API_KEY';
const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${source.latitude},${source.longitude}&destination=${destination.latitude},${destination.longitude}&key=${API_KEY}`;
const response = await fetch(url);
const json = await response.json();
if (json.routes.length) {
const points = decodePolyline(json.routes[0].overview_polyline.points);
return points;
} else {
Alert.alert('Ruta no encontrada');
return [];
}
} catch (error) {
Alert.alert('Error obteniendo ruta', String(error));
return [];
}
};
// Decodificador de polilínea personalizado
const decodePolyline = (t: string, e = 5) => {
let points = [];
let index = 0, lat = 0, lng = 0;
while (index < t.length) {
let b, shift = 0, result = 0;
do {
b = t.charCodeAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
let dlat = (result & 1) ? ~(result >> 1) : result >> 1;
lat += dlat;
shift = 0;
result = 0;
do {
b = t.charCodeAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
let dlng = (result & 1) ? ~(result >> 1) : result >> 1;
lng += dlng;
points.push({
latitude: lat / 1e5,
longitude: lng / 1e5,
});
}
return points;
};
Características avanzadas para implementar Google Maps en React Native
1. Clustering de marcadores
Para manejar múltiples marcadores eficientemente:
import { Marker } from 'react-native-maps';
import SuperCluster from 'supercluster';
const MarkerCluster = ({ markers, region, onRegionChange }) => {
const [clusters, setClusters] = useState([]);
useEffect(() => {
const cluster = new SuperCluster({
radius: 40,
maxZoom: 16,
});
cluster.load(markers.map(marker => ({
type: 'Feature',
properties: { cluster: false, marker },
geometry: {
type: 'Point',
coordinates: [marker.longitude, marker.latitude],
},
})));
const bbox = [
region.longitude - region.longitudeDelta,
region.latitude - region.latitudeDelta,
region.longitude + region.longitudeDelta,
region.latitude + region.latitudeDelta,
];
const clusters = cluster.getClusters(bbox, Math.round(Math.log(360 / region.longitudeDelta) / Math.LN2));
setClusters(clusters);
}, [markers, region]);
return (
<>
{clusters.map((cluster, index) => (
<Marker
key={index}
coordinate={{
longitude: cluster.geometry.coordinates[0],
latitude: cluster.geometry.coordinates[1],
}}
title={cluster.properties.cluster ? `${cluster.properties.point_count} ubicaciones` : cluster.properties.marker.title}
/>
))}
</>
);
};
2. Estilos de mapa personalizados
Para personalizar la apariencia al implementar Google Maps en React Native:
const customMapStyle = [
{
"elementType": "geometry",
"stylers": [{ "color": "#242f3e" }]
},
{
"elementType": "labels.text.stroke",
"stylers": [{ "color": "#242f3e" }]
},
{
"elementType": "labels.text.fill",
"stylers": [{ "color": "#746855" }]
},
// Más estilos...
];
<MapView
style={{ flex: 1 }}
provider="google"
customMapStyle={customMapStyle}
region={region}
>
3. Geocodificación inversa
Para obtener direcciones a partir de coordenadas:
const reverseGeocode = async (latitude: number, longitude: number) => {
try {
const API_KEY = 'YOUR_GOOGLE_MAPS_API_KEY';
const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${API_KEY}`;
const response = await fetch(url);
const json = await response.json();
if (json.results.length > 0) {
return json.results[0].formatted_address;
}
return 'Dirección no encontrada';
} catch (error) {
console.error('Error en geocodificación inversa:', error);
return 'Error obteniendo dirección';
}
};
4. Mapas offline
Para funcionalidad offline al implementar Google Maps en React Native:
import { OfflineMap } from 'react-native-maps';
const OfflineMapScreen = () => {
const downloadOfflineMap = async (region: Region) => {
try {
await OfflineMap.downloadMap({
region,
name: 'offline_map_madrid',
});
Alert.alert('Éxito', 'Mapa descargado para uso offline');
} catch (error) {
Alert.alert('Error', 'No se pudo descargar el mapa offline');
}
};
return (
<View style={{ flex: 1 }}>
<MapView
style={{ flex: 1 }}
provider="google"
offlineMapName="offline_map_madrid"
region={region}
/>
</View>
);
};
Optimización de performance al implementar Google Maps en React Native
1. Lazy loading de marcadores
const LazyMarkers = ({ markers, region }) => {
const visibleMarkers = useMemo(() => {
return markers.filter(marker => {
return (
marker.latitude >= region.latitude - region.latitudeDelta &&
marker.latitude <= region.latitude + region.latitudeDelta &&
marker.longitude >= region.longitude - region.longitudeDelta &&
marker.longitude <= region.longitude + region.longitudeDelta
);
});
}, [markers, region]);
return (
<>
{visibleMarkers.map(marker => (
<Marker key={marker.id} coordinate={marker} />
))}
</>
);
};
2. Debounce para actualizaciones de región
import { debounce } from 'lodash';
const MapScreen = () => {
const debouncedRegionChange = useMemo(
() => debounce((region: Region) => {
// Actualizar marcadores visibles
updateVisibleMarkers(region);
}, 300),
[]
);
return (
<MapView
onRegionChangeComplete={debouncedRegionChange}
// otras props...
/>
);
};
Testing y debugging al implementar Google Maps en React Native
1. Testing de componentes de mapa
import { render } from '@testing-library/react-native';
import MapScreen from '../MapScreen';
jest.mock('react-native-maps', () => ({
MapView: 'MapView',
Marker: 'Marker',
Polyline: 'Polyline',
}));
describe('MapScreen', () => {
test('renders map with correct props', () => {
const { getByTestId } = render(<MapScreen />);
const mapView = getByTestId('map-view');
expect(mapView).toBeTruthy();
});
});
2. Debugging de permisos
const debugPermissions = async () => {
if (Platform.OS === 'android') {
const permissions = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
]);
console.log('Android permissions:', permissions);
} else {
const result = await check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
console.log('iOS location permission:', result);
}
};
Mejores prácticas al implementar Google Maps en React Native
1. Gestión de memoria
const MapScreen = () => {
useEffect(() => {
return () => {
// Limpiar listeners y timers
if (watchId) {
Geolocation.clearWatch(watchId);
}
};
}, []);
};
2. Manejo de errores robusto
const ErrorBoundary = ({ children }) => {
const [hasError, setHasError] = useState(false);
useEffect(() => {
const errorHandler = (error) => {
console.error('Map error:', error);
setHasError(true);
};
// Configurar manejo de errores globales
return () => {
// Limpiar
};
}, []);
if (hasError) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Error cargando el mapa. Intenta de nuevo.</Text>
</View>
);
}
return children;
};
3. Configuración de producción
const isProduction = __DEV__ === false;
const MapScreen = () => {
const mapConfig = {
provider: 'google',
showsUserLocation: !isProduction, // Solo en desarrollo
showsMyLocationButton: !isProduction,
loadingEnabled: true,
loadingIndicatorColor: '#1E90FF',
pitchEnabled: true,
rotateEnabled: true,
scrollEnabled: true,
zoomEnabled: true,
};
return (
<MapView
{...mapConfig}
style={{ flex: 1 }}
region={region}
/>
);
};
Conclusión: dominar cómo implementar Google Maps en React Native
Ahora tenemos una aplicación React Native CLI completamente configurada que utiliza Google Maps para mostrar rutas usando la Directions API. Esta configuración incluye permisos de ubicación, recuperación de direcciones y renderizado de polylines.
Esta base robusta es ideal para implementar casos de uso avanzados como:
- Seguimiento de entregas con actualizaciones en tiempo real
- Reserva de taxis con rutas dinámicas
- Asistencia de navegación paso a paso
- Aplicaciones de turismo con puntos de interés
- Fitness tracking con rutas grabadas
- Marketplace de servicios con proveedores cercanos
Extensiones adicionales al implementar Google Maps en React Native:
- React-native-maps con geolocalización para tracking continuo
- Controles de mapa personalizados para UX específica
- Soporte de mapas offline para funcionalidad sin conexión
- Integración con notificaciones push para actualizaciones de ubicación
- Analytics de uso para optimizar rutas frecuentes
En Juice Studio, siempre elegimos las herramientas adecuadas para cada proyecto. Nuestra experiencia en implementar Google Maps en React Native nos permite crear soluciones robustas que van desde mapas básicos hasta sistemas complejos de navegación y tracking.
Ya sea que estés construyendo una aplicación de delivery, una plataforma de movilidad o cualquier solución que requiera mapas interactivos, podemos ayudarte a implementar Google Maps en React Native de manera eficiente y escalable.
¿Listo para construir la próxima gran aplicación con mapas? En Juice Studio te ayudamos a implementar Google Maps en React Native con las mejores prácticas y tecnologías más avanzadas.
Construyamos algo excepcional juntos aplicando todo el potencial de implementar Google Maps en React Native en tu próximo proyecto móvil.