Hướng dẫn tích hợp Flutter SDK
Hướng dẫn tích hợp GPS Tracking cho ứng dụng Flutter mobile (iOS & Android).
Package: vietmap_tracking_plugin
Yêu cầu hệ thống
| Minimum | |
|---|---|
| Flutter | 3.3.0+ |
| Dart | 3.8.0+ |
| Android | API 21+ (Android 5.0) |
| iOS | 15.0+ |
1. Cài đặt
pubspec.yaml
dependencies:
vietmap_tracking_plugin: ^1.0.7flutter pub get2. Cấu hình Platform
Android — android/app/src/main/AndroidManifest.xml
<manifest>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<service
android:name=".LocationTrackingService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
</application>
</manifest>iOS — ios/Runner/Info.plist
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Ứng dụng cần GPS để theo dõi vị trí nhân viên khi đang chạy.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Ứng dụng cần GPS để tiếp tục theo dõi khi chạy nền.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Ứng dụng cần GPS khi chạy nền.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>background-fetch</string>
<string>background-processing</string>
</array>
<!-- Bắt buộc cho background tracking trên iOS -->
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.vietmaptrackingsdk.background-location</string>
<string>com.vietmaptrackingsdk.location-sync</string>
</array>
</dict>3. Khởi tạo SDK
import 'package:vietmap_tracking_plugin/vietmap_tracking_plugin.dart';
final controller = VietmapTrackingController.instance;
// Khởi tạo với API key
// baseURL là optional — SDK dùng URL production mặc định
await controller.initializeTracking('YOUR_API_KEY');
// Hoặc với custom server:
await controller.initializeTracking(
'YOUR_API_KEY',
baseURL: 'https://live.fleetwork.vn/api/v1',
);
// Đăng ký lifecycle observer (gọi 1 lần sau initializeTracking)
controller.registerLifecycleObserver();Gọi
initializeTracking()trước tất cả method khác. ThrowsINVALID_API_KEYnếu key không hợp lệ.
4. Quyền truy cập GPS
Bước 1 — Quyền foreground (khi app đang chạy):
final result = await controller.requestLocationPermissions();
print(result.granted); // bool — đủ quyền foreground?
print(result.status.value); // "granted" | "denied" | "not_granted"
print(result.fineLocation); // bool
print(result.backgroundLocation); // boolBước 2 — Quyền background (bắt buộc để tiếp tục theo dõi khi màn hình tắt):
final status = await controller.requestAlwaysLocationPermissions();
// "granted" | "denied" | "when_in_use"Kiểm tra trạng thái quyền (không hiện popup):
final result = await controller.hasLocationPermissions();
if (!result.granted) {
// Dẫn user đến màn hình cấp quyền
}5. Bắt đầu / Dừng theo dõi
// Bắt đầu tracking — timer mode (mỗi 10 giây)
await controller.startTracking(
LocationTrackingConfig(
intervalMs: 10000, // Capture GPS mỗi 10 giây
distanceFilter: 0, // Tắt distance trigger
accuracy: LocationAccuracy.high,
backgroundMode: true,
userId: 'user-001',
vehicleId: 'vehicle-abc', // optional
deviceId: 'device-001', // optional
notificationTitle: 'Đang theo dõi hành trình', // Android only
notificationMessage: 'Chạm để mở ứng dụng', // Android only
),
);
// Bắt đầu tracking — distance mode (mỗi 15 mét)
await controller.startTracking(
LocationTrackingConfig(
intervalMs: 0, // Tắt timer trigger
distanceFilter: 15.0, // Capture mỗi khi di chuyển ≥ 15m
accuracy: LocationAccuracy.high,
backgroundMode: true,
userId: 'user-001',
),
);
// Dừng tracking
await controller.stopTracking();LocationTrackingConfig — Tham số
| Tham số | Kiểu | Mô tả |
|---|---|---|
intervalMs | int | Khoảng thời gian giữa 2 GPS points (ms). 0 = tắt timer. |
distanceFilter | double | Khoảng cách tối thiểu để capture point mới (mét). ≤ 0 = tắt. |
accuracy | LocationAccuracy | high / medium / low. |
backgroundMode | bool | true = tiếp tục tracking khi app ở background. |
userId | String? | ID nhân viên. |
vehicleId | String? | ID xe. |
notificationTitle | String? | Android: tiêu đề notification foreground service. |
notificationMessage | String? | Android: nội dung notification. |
allowMockLocation | bool | true = cho phép mock location. false = bật phát hiện GPS giả. |
Lưu ý: Chỉ dùng một trong hai —
intervalMs > 0hoặcdistanceFilter > 0. Không kết hợp cả hai.
6. Kiểm tra trạng thái
final isActive = await controller.isTrackingActive(); // bool
final status = await controller.getTrackingStatus();
print(status.isTracking); // bool
print(status.trackingDuration); // int (ms kể từ startTracking)
print(status.lastLocationUpdate); // int? (unix ms)7. Cập nhật cấu hình khi đang tracking
Thay đổi config mà không cần restart tracking:
await controller.updateTrackingConfig(
LocationTrackingConfig(
intervalMs: 30000,
distanceFilter: 0,
accuracy: LocationAccuracy.medium,
backgroundMode: true,
),
);8. Cập nhật thông tin phương tiện
// Cập nhật ngay — không cần restart tracking
await controller.setVehicleId('vehicle-xyz');
await controller.setDriverId('user-002');
// Lấy giá trị hiện tại
final vehicleId = await controller.getVehicleId();
final driverId = await controller.getDriverId();9. Smart Battery Optimization
SDK tự động chuyển đổi giữa 3 profiles tracking dựa trên chuyển động và mức pin:
| Profile | Interval | Khi nào áp dụng |
|---|---|---|
navigation | 5 giây | Đang vào góc cua (cần chính xác cao) |
general | 30 giây | Di chuyển thẳng / đứng yên |
batterySaver | 10 phút | Pin < 15% và không sạc |
// Đặt preferred profile khi xe đang di chuyển
controller.setSmartBatteryPreferredProfile(SmartBatteryProfile.navigation);
// Feed GPS vào SmartBattery để phát hiện góc cua và đứng yên
controller.onLocationUpdate.listen((LocationData loc) {
controller.feedLocationToSmartBattery(loc.speed, heading: loc.heading);
});
// Lắng nghe khi profile thay đổi
controller.onSmartBatteryProfileChanged.listen((SmartBatteryProfile profile) {
print('Profile changed: ${profile.name}');
});10. Offline Cache
SDK tự động lưu local khi mất mạng và gửi lại khi có kết nối.
// Cấu hình giới hạn cache (gọi trước startTracking)
await controller.configureCacheLimits(
maxRecords: 5000, // Số record tối đa
maxDbSizeBytes: 52428800, // 50 MB
batchSize: 50, // Record mỗi lần upload
);
// Kiểm tra / quản lý cache
final count = await controller.getCachedLocationsCount(); // số điểm chờ gửi
final bytes = await controller.getDatabaseSizeBytes(); // kích thước cache
// Gửi cache ngay (không chờ timer 30s nội bộ)
await controller.uploadCachedLocationsManually();
// Bật / tắt auto upload
await controller.setAutoUpload(true);
// Xoá toàn bộ cache (không hoàn tác)
await controller.clearCachedLocations();11. Event Streams
// Vị trí mới ghi nhận
controller.onLocationUpdate.listen((LocationData loc) {
print('${loc.latitude}, ${loc.longitude}');
print('Speed: ${loc.speed} m/s'); // m/s
print('Heading: ${loc.heading}°');
});
// Thay đổi trạng thái tracking
controller.onTrackingStatusChanged.listen((TrackingStatus status) {
print('isTracking: ${status.isTracking}');
});
// Profile Smart Battery thay đổi
controller.onSmartBatteryProfileChanged.listen((SmartBatteryProfile profile) {
print('Battery profile: ${profile.name}');
});12. Data Models
LocationData
| Field | Kiểu | Đơn vị | Mô tả |
|---|---|---|---|
latitude | double | độ | Vĩ độ. |
longitude | double | độ | Kinh độ. |
altitude | double | mét | Độ cao. |
accuracy | double | mét | Độ chính xác GPS. |
speed | double | m/s | Tốc độ tức thời. |
heading | double | 0–360° | Hướng đầu xe (0=Bắc). |
timestamp | int | unix ms | Thời điểm capture. |
dateTime | DateTime | — | Getter từ timestamp. |
Lưu ý:
speedđơn vị là m/s (mét/giây). Để chuyển sang km/h:speed * 3.6.
TrackingStatus
| Field | Kiểu | Mô tả |
|---|---|---|
isTracking | bool | Tracking đang chạy. |
lastLocationUpdate | int? | Unix ms lần GPS gần nhất. |
trackingDuration | int | ms kể từ startTracking(). |
lastUpdateTime | DateTime? | Getter từ lastLocationUpdate. |
duration | Duration | Getter từ trackingDuration. |
PermissionResult
| Field | Kiểu | Mô tả |
|---|---|---|
granted | bool | Đủ quyền. |
status | PermissionStatus | .granted / .denied / .notGranted. |
fineLocation | bool | Quyền GPS chính xác. |
coarseLocation | bool | Quyền GPS xấp xỉ. |
backgroundLocation | bool | Quyền background. |
SmartBatteryProfile
enum SmartBatteryProfile {
navigation, // 5 giây — chính xác cao
general, // 30 giây — cân bằng mặc định
batterySaver, // 10 phút — tiết kiệm pin tối đa
}13. Full API Reference
Khởi tạo
| Method / Property | Return | Mô tả |
|---|---|---|
initializeTracking(apiKey, {baseURL}) | Future<void> | Xác thực key phía server. Throws INVALID_API_KEY nếu thất bại. |
setMetadata(Map) | Future<void> | Đính kèm key-value tùy ý vào mỗi bản ghi GPS. |
configureAlertAPI(apiKey, apiID) | Future<void> | Cấu hình thông tin đăng nhập cảnh báo tốc độ. |
setFakeGpsNotificationConfig({title, message}) | Future<void> | Tùy chỉnh thông báo GPS giả native của SDK. |
registerLifecycleObserver() | void | Bật chuyển đổi SDK background/foreground tự động. |
Điều khiển Tracking
| Method / Property | Return | Mô tả |
|---|---|---|
startTracking(config) | Future<bool> | Bắt đầu GPS tracking. userId là bắt buộc. |
stopTracking() | Future<bool> | Dừng GPS tracking. |
isTrackingActive() | Future<bool> | Tracking đang chạy? |
getCurrentLocation() | Future<LocationData?> | Vị trí gần nhất đã biết. null nếu chưa có tín hiệu. |
getTrackingStatus() | Future<TrackingStatus> | Snapshot trạng thái hiện tại. |
updateTrackingConfig(config) | Future<bool> | Áp dụng config mới cho phiên đang chạy. |
getTrackingHealthStatus() | Future<Map> | Snapshot chẩn đoán: mạng, cache, SDK flags. |
Quyền
| Method / Property | Return | Mô tả |
|---|---|---|
requestLocationPermissions() | Future<PermissionResult> | Xin quyền foreground. |
requestAlwaysLocationPermissions() | Future<String> | Xin quyền background. |
hasLocationPermissions() | Future<PermissionResult> | Kiểm tra quyền, không popup. |
GPS giả
| Method / Property | Return | Mô tả |
|---|---|---|
setFakeGpsPolicy(policy) | Future<void> | Đặt chính sách xử lý GPS giả. |
onFakeGpsDetected | Stream<FakeGpsEvent> | Stream sự kiện GPS giả. |
Event Streams
| Method / Property | Return | Mô tả |
|---|---|---|
onLocationUpdate | Stream<LocationData> | Stream GPS points. |
onTrackingStatusChanged | Stream<TrackingStatus> | Stream trạng thái tracking. |
onFakeGpsDetected | Stream<FakeGpsEvent> | Stream sự kiện GPS giả. |
onSmartBatteryProfileChanged | Stream<SmartBatteryProfile> | Stream thay đổi profile. |
Smart Battery
| Method / Property | Return | Mô tả |
|---|---|---|
setSmartBatteryPreferredProfile(profile) | void | Đặt preferred profile khi di chuyển. |
Cache & Đồng bộ
| Method / Property | Return | Mô tả |
|---|---|---|
setAutoUpload(enabled) | Future<bool> | Bật/tắt auto upload. |
isNetworkConnected() | Future<bool> | Kết nối mạng. |
getCachedLocationsCount() | Future<int> | Số records pending. |
getDatabaseSizeBytes() | Future<int> | Kích thước cache (bytes). |
uploadCachedLocationsManually() | Future<bool> | Flush cache ngay. |
clearCachedLocations() | Future<bool> | Xoá toàn bộ cache. |
configureCacheLimits(...) | Future<bool> | Cấu hình giới hạn cache. |
Lịch sử
| Method / Property | Return | Mô tả |
|---|---|---|
getTrackingHistory({userId, fromTimestamp, toTimestamp, pageNumber, pageSize, sortDescending}) | Future<List<GpsLocation>> | Lấy lịch sử GPS theo bộ lọc. |
14. Fake GPS Detection
SDK có cơ chế phát hiện fake GPS / mock location ở native layer.
Hệ điều hành nào hỗ trợ
| Nền tảng | Cơ chế phát hiện |
|---|---|
| iOS 15+ | CLLocationSourceInformation |
| iOS 11–14 | Không có API hệ thống để phát hiện trong SDK |
| Android 12+ | Location.isMock() |
| Android 18–30 | Location.isFromMockProvider() |
iOS
Trên iOS 15 trở lên, SDK đọc location.sourceInformation từ CLLocation để xác định vị trí có bị giả lập hay không.
Các trường hợp SDK có thể phát hiện:
| Reason | Ý nghĩa |
|---|---|
simulatedBySoftware | Vị trí được giả lập bởi phần mềm. |
producedByAccessory | Vị trí được tạo từ thiết bị / accessory bên ngoài. |
Khi phát hiện, SDK sẽ fire event onFakeGPSDetected với payload:
| Key | Kiểu | Mô tả |
|---|---|---|
isFake | bool | Luôn là true khi event được gọi. |
reason | string | simulatedBySoftware hoặc producedByAccessory. |
lat | double | Vĩ độ của điểm bị phát hiện là fake. |
lng | double | Kinh độ của điểm bị phát hiện là fake. |
timestamp | double | Unix timestamp theo giây. |
Giới hạn: iOS 11–14 không có
CLLocationSourceInformation, nên SDK không thể phát hiện fake GPS trên các phiên bản này.
Android
Trên Android, SDK kiểm tra mock location ngay khi nhận được GPS point từ hệ điều hành:
- Android 12+ dùng
location.isMock() - Android 18–30 dùng
location.isFromMockProvider()
Khi phát hiện, SDK fire callback native FakeGPSCallback.onFakeGPSDetected(lat, lng).
Lưu ý: kiểm tra fake GPS diễn ra trước bước
shouldProcessLocation(). Điều đó có nghĩa là dù point sau đó bị skip do timing filter hoặc distance filter, callback fake GPS vẫn được gọi nếu hệ thống đánh dấu đó là mock location.
Khuyến nghị cho ứng dụng tích hợp
Khi tính năng Flutter forward hoàn tất, ứng dụng nên tự quyết định cách xử lý, ví dụ:
- hiển thị cảnh báo cho người dùng
- ghi log để kiểm tra nội bộ
- gắn cờ bản ghi để backend review
- vẫn cho tracking tiếp tục nhưng đánh dấu điểm nghi ngờ