react-native-custom-map
Version:
A custom map component for React Native and Expo projects using Google Maps via WebView.
118 lines (107 loc) • 3.04 kB
JavaScript
import React, { useEffect, useRef } from "react";
import { StyleSheet, View } from "react-native";
import { WebView } from "react-native-webview";
import PropTypes from "prop-types";
const MapView = ({
latitude,
longitude,
zoom = 10,
markers = [],
apiKey,
onMarkerDragEnd,
}) => {
const webViewRef = useRef(null);
const googleMapsHTML = `
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style>
html, body, #map { height: 100%; margin: 0; padding: 0; }
</style>
<script src="https://maps.googleapis.com/maps/api/js?key=${apiKey}"></script>
<script>
let map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: { lat: ${latitude}, lng: ${longitude} },
zoom: ${zoom}
});
${markers
.map(
(marker) => `
var marker = new google.maps.Marker({
position: {lat: ${marker.lat}, lng: ${marker.lng}},
map: map,
title: '${marker.title}',
draggable: true
});
google.maps.event.addListener(marker, 'dragend', function(event) {
window.ReactNativeWebView.postMessage(JSON.stringify({
title: marker.title,
lat: event.latLng.lat(),
lng: event.latLng.lng()
}));
});
`
)
.join("")}
}
function updateMapCenter(lat, lng) {
if (map) {
map.setCenter({ lat, lng });
}
}
</script>
</head>
<body onload="initMap()">
<div id="map"></div>
</body>
</html>
`;
useEffect(() => {
if (webViewRef.current) {
const script = `updateMapCenter(${latitude}, ${longitude});`;
webViewRef.current.injectJavaScript(script);
}
}, [latitude, longitude]);
return (
<View style={styles.container}>
<WebView
ref={webViewRef}
originWhitelist={["*"]}
source={{ html: googleMapsHTML }}
style={styles.map}
onMessage={(event) => {
const data = JSON.parse(event.nativeEvent.data);
if (onMarkerDragEnd) {
onMarkerDragEnd(data);
}
}}
/>
</View>
);
};
MapView.propTypes = {
latitude: PropTypes.number.isRequired,
longitude: PropTypes.number.isRequired,
zoom: PropTypes.number,
markers: PropTypes.arrayOf(
PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired,
title: PropTypes.string,
})
),
apiKey: PropTypes.string.isRequired,
onMarkerDragEnd: PropTypes.func,
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
flex: 1,
},
});
export default MapView;