![]()
Hãy thử tưởng tượng một ngày bạn mở ứng dụng Grab lên để gọi xe, nhưng thay vì nhìn thấy bản đồ trực quan với những chiếc xe đang di chuyển, bạn chỉ nhận được một danh sách các con số tọa độ khô khan. Chắc chắn bạn sẽ xóa app ngay lập tức!
Trong kỷ nguyên của các dịch vụ theo yêu cầu (On-demand services), Maps không còn là một tính năng xa xỉ, mà là tiêu chuẩn bắt buộc.
Ở những bài trước, chúng ta đã biết cách lấy những con số tọa độ vô tri (Kinh độ, Vĩ độ) thông qua expo-location. Hôm nay, trong bài học này, chúng ta sẽ biến những con số đó thành một trải nghiệm thị giác tuyệt vời bằng cách vẽ chúng lên bản đồ kỹ thuật số với thư viện react-native-maps.
1. Cài đặt thư viện cần thiết
react-native-maps là thư viện tiêu chuẩn vàng được cộng đồng sử dụng để hiển thị bản đồ trong React Native. Nó được bảo trợ bởi chính cộng đồng Expo, nên việc tích hợp cực kỳ trơn tru.

Mở Terminal và chạy lệnh sau để đưa thư viện vào dự án của bạn:
npx expo install react-native-maps
Sự thật thú vị về hệ điều hành: Theo mặc định, thư viện này sẽ hiển thị Apple Maps trên các thiết bị iPhone/iPad, và hiển thị Google Maps trên các thiết bị Android. Điều này giúp ứng dụng của bạn có cảm giác "chuẩn Native" nhất đối với thói quen của từng tệp người dùng.
2. Khởi tạo "Tấm bản đồ" đầu tiên
Việc gọi bản đồ ra màn hình dễ đến mức bạn sẽ phải ngạc nhiên. Khác với lập trình Web (nơi bạn phải chèn iframe hay dùng SDK phức tạp), trong React Native, bản đồ hoạt động y hệt như một Core Component (như thẻ View hay Image).
Hãy tạo một file app/map.tsx và nhập đoạn code sau:
import React from 'react'
import { StyleSheet, View } from 'react-native'
import MapView from 'react-native-maps'
export default function MapScreen() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
// Thiết lập vị trí khởi tạo (Ví dụ: Hồ Gươm, Hà Nội)
initialRegion={{
latitude: 21.0285, // Vĩ độ
longitude: 105.8542, // Kinh độ
latitudeDelta: 0.05, // Độ zoom của bản đồ theo trục dọc (Càng nhỏ càng zoom sát)
longitudeDelta: 0.05, // Độ zoom của bản đồ theo trục ngang
}}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1, // Bắt buộc phải có để thẻ View cha chiếm hết màn hình
},
map: {
width: '100%',
height: '100%', // Bản đồ sẽ trải dài toàn bộ màn hình
},
})
Lưu ý: Bất kỳ thẻ <MapView> nào cũng bắt buộc phải được cấp thuộc tính kích thước (width, height hoặc flex: 1). Nếu thiếu, bản đồ sẽ tàng hình!
3. Ghim vị trí trực quan bằng Marker
Bản đồ trống rỗng thì không có ý nghĩa gì cả. Bạn cần cắm các "chiếc ghim" lên đó để chỉ định vị trí cửa hàng, vị trí tài xế, hoặc nơi giao hàng. Chúng ta sử dụng component Marker.
Thẻ <Marker> phải luôn được đặt bên trong (làm con) của thẻ <MapView>.
import React from 'react'
import { StyleSheet, View, Text } from 'react-native'
import MapView, { Marker } from 'react-native-maps'
export default function StoreMap() {
const storeLocation = { latitude: 21.0285, longitude: 105.8542 }
return (
<View style={styles.container}>
<MapView style={styles.map} initialRegion={{ ...storeLocation, latitudeDelta: 0.01, longitudeDelta: 0.01 }}>
{/* Cắm cờ vị trí cửa hàng */}
<Marker coordinate={storeLocation} title="Cửa hàng trung tâm" description="Mở cửa từ 8h - 22h" />
</MapView>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1 },
map: { flex: 1 },
})
Khi người dùng chạm vào chiếc ghim đỏ trên màn hình, một khung thông tin (Callout) sẽ bật lên hiển thị title và description cực kỳ chuyên nghiệp.
4. Tùy biến giao diện (Dark Mode & Custom Marker)
Để thiết kế UI/UX theo hơi hướng hiện đại, bạn không nên sử dụng chiếc ghim màu đỏ mặc định nhàm chán. react-native-maps cho phép bạn tùy biến mọi thứ.
- Đổi Icon của Marker: Bạn chỉ cần truyền một thẻ
<Image>hoặc các thẻ vẽ đồ họa vào bên trong<Marker>. - Kích hoạt Dark Mode cho bản đồ: Nếu ứng dụng của bạn có nền tối, bản đồ sáng chói sẽ làm hỏng trải nghiệm thị giác. Bạn có thể truyền một mảng JSON định dạng màu sắc vào thuộc tính
customMapStyle.
<Marker coordinate={storeLocation}>
{/* Thay ghim đỏ bằng một Icon tùy biến, ưu tiên phong cách đồ họa phẳng */}
<View style={{ backgroundColor: '#2d3436', padding: 8, borderRadius: 20 }}>
<Text style={{ color: '#fff', fontWeight: 'bold' }}>📍 CHÍNH</Text>
</View>
</Marker>
5. Kết hợp GPS: Hiển thị vị trí Real-time
Bây giờ là lúc chúng ta ghép nối kiến thức ở bài expo-location vào tấm bản đồ này.
Mục tiêu: Mở app lên, tự động xin quyền, lấy tọa độ GPS hiện tại và dịch chuyển bản đồ về đúng chỗ bạn đang đứng.
import React, { useState, useEffect } from 'react'
import { View, StyleSheet, ActivityIndicator, Text } from 'react-native'
import MapView, { Marker } from 'react-native-maps'
import * as Location from 'expo-location'
export default function LiveMapScreen() {
const [myLocation, setMyLocation] = useState(null)
const [errorMsg, setErrorMsg] = useState(null)
useEffect(() => {
;(async () => {
// 1. Xin quyền GPS
let { status } = await Location.requestForegroundPermissionsAsync()
if (status !== 'granted') {
setErrorMsg('Không thể hiển thị bản đồ vì thiếu quyền GPS.')
return
}
// 2. Lấy tọa độ hiện tại
let location = await Location.getCurrentPositionAsync({})
setMyLocation({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
})
})()
}, [])
// Nếu đang chờ GPS, hiển thị loading
if (!myLocation && !errorMsg) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#0984e3" />
<Text>Đang dò vệ tinh GPS...</Text>
</View>
)
}
// Nếu bị lỗi từ chối quyền
if (errorMsg) {
return (
<View style={styles.center}>
<Text style={{ color: 'red' }}>{errorMsg}</Text>
</View>
)
}
// Hoàn tất: Vẽ bản đồ
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={myLocation} // Dùng region thay vì initialRegion để bản đồ tự động di chuyển khi myLocation thay đổi
showsUserLocation={true} // Bật chấm xanh lam hiển thị hướng điện thoại (Native feature)
showsMyLocationButton={true} // Bật nút bấm để tự động quay về tâm
>
{/* Bạn có thể cắm thêm các Marker khác ở đây */}
</MapView>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1 },
map: { flex: 1 },
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
})
6. Lưu ý quan trọng khi triển khai (Deployment)
Việc chạy bản đồ trên ứng dụng Expo Go (khi đang code) rất mượt mà vì Expo đã cấu hình sẵn mọi thứ. Tuy nhiên, khi bạn đóng gói ứng dụng (Build App) để đẩy lên Google Play Store hoặc Apple App Store, bản đồ có thể bị lỗi "Trắng màn hình".
Để khắc phục, bạn BẮT BUỘC phải tạo Google Maps API Key (miễn phí) từ Google Cloud Console và chèn vào file app.json của dự án.
Cấu hình trong app.json sẽ trông như sau:
{
"expo": {
"ios": {
"bundleIdentifier": "com.yourcompany.app",
"config": {
"googleMapsApiKey": "MÃ_API_KEY_IOS_CỦA_BẠN"
}
},
"android": {
"package": "com.yourcompany.app",
"config": {
"googleMaps": {
"apiKey": "MÃ_API_KEY_ANDROID_CỦA_BẠN"
}
}
}
}
}
Kết luận: Nắm trọn "công nghệ tỷ đô"
Giao diện trực quan làm nên đẳng cấp của ứng dụng. Bằng cách kết hợp react-native-maps và expo-location, bạn đã nắm trong tay công nghệ lõi của những nền tảng tỷ đô.
- Khởi tạo
<MapView>vớiflex: 1để hiển thị bản đồ. - Dùng
<Marker>để đánh dấu các điểm cần lưu ý. - Luôn lấy quyền GPS trước khi sử dụng thuộc tính
showsUserLocation.
Hẹn gặp lại bạn ở những bài học thú vị tiếp theo!