adaptive_location_tracker 0.1.1
adaptive_location_tracker: ^0.1.1 copied to clipboard
A battery-efficient adaptive location tracker with offline sync, Kalman filtering, and foreground service support.
Adaptive Location Tracker 📍 #
A "Gold Standard" location tracking plugin for Flutter, designed for battery efficiency, high accuracy, and offline resilience. It creates a foreground service on Android to track location reliably in the background, making it perfect for field force, delivery, and logistics applications.
✨ Features #
- 🔋 Battery Efficient: Uses Google's
FusedLocationProviderClientand partial wake locks to ensure reliability without excessive drain. - 🧠 Intelligent Filtering:
- Spiderweb Prevention: Automatically "Locks" location when stationary (e.g., inside a building) to prevent random GPS jumps.
- Breakout Detection: Uses a sequence of verified moves to resume tracking, ensuring accuracy in rural or urban canyon areas.
- Impossible Speed Filter: Rejects impossible jumps in location (e.g., GPS glitching 500m in 1 second).
- 📡 Robust Syncing:
- Batch Uploads: Sends multiple locations (up to 100) in a single HTTP request to save battery and reduce server load.
- Offline Buffer: Automatically saves coordinates to a local SQLite database when the network is unavailable.
- Exponential Backoff: Retries failed syncs with increasing delays (2s, 4s, 8s...) to handle server downtime gracefully.
- ⚠️ Status Reporting: Automatically reports to a specific URL when the user turns GPS on or off.
- 🏁 Managed Checkout: Dedicated checkout flow that attempts to flush all pending data and reports final success or failure through events.
- 🌐 Dual Mode: Supports both HTTP/REST and WebSockets for real-time tracking.
🚀 Installation #
Add the package to your pubspec.yaml:
dependencies:
adaptive_location_tracker: ^0.1.1
Android Setup #
The plugin handles most permissions, but ensure your AndroidManifest.xml doesn't conflict. The plugin requires:
ACCESS_FINE_LOCATIONACCESS_BACKGROUND_LOCATION(Mandatory for background tracking)FOREGROUND_SERVICEFOREGROUND_SERVICE_LOCATIONACTIVITY_RECOGNITION(for Motion Detection)REQUEST_IGNORE_BATTERY_OPTIMIZATIONS(Highly recommended)
⚠️ Important: Android Background Restrictions #
🔋 Battery Optimizations #
Android OS is very aggressive at killing background services to save power. Recommendation:
- The app should request to ignore battery optimizations:
Permission.ignoreBatteryOptimizations.request(). - Guide users to set the app to "Don't Optimize" or "Unrestricted" in system settings.
📱 Location "Always" #
On Android 11+, users must manually select "Allow all the time" in the app's location settings. Background tracking will fail or be severely throttled if only "While using the app" is selected.
📖 Usage #
1. Start Tracking #
Call start() with your configuration. It returns a bool indicating if the service was successfully initiated.
bool success = await AdaptiveLocationTracker.start(
url: 'https://your-api.com/v1/trace',
reportLocationUrl: 'https://your-api.com/v1/report-location',
headers: {'Authorization': 'Bearer YOUR_TOKEN'},
intervalSeconds: 15,
extraData: {'user_id': 101},
);
2. Listen to Real-time Events (Stream) #
Subscribe to the static eventsStream broadcast to receive real-time updates from the background service (such as smooth coordinates, motion state transitions, sync successes, checkout outcomes, or runtime errors):
import 'dart:async';
import 'package:adaptive_location_tracker/adaptive_location_tracker.dart';
StreamSubscription<Map<String, dynamic>>? _subscription;
void startListening() {
_subscription = AdaptiveLocationTracker.eventsStream.listen((event) {
final String type = event['type'];
switch (type) {
case 'location':
// Real-time Kalman-smoothed approved coordinate
print("Approved: ${event['latitude']}, ${event['longitude']} (Accuracy: ${event['accuracy']}m)");
break;
case 'trip_state':
// State transition updates: 'MOVING' or 'STATIONARY'
print("Trip State changed to: ${event['state']}");
break;
case 'sync_success':
// Successful remote batch sync event
print("Successfully flushed ${event['count']} records to the server.");
break;
case 'checkout_success':
// Checkout completed and all pending points were flushed
print(event['message']);
break;
case 'error':
// Real-time error reports
print("Native error [${event['code']}]: ${event['message']}");
break;
}
});
}
void stopListening() {
_subscription?.cancel();
}
3. Checkout (Safe Stop) #
Starts a flush of all locally stored points before shutting down. Use this for "End Shift" buttons.
bool checkoutStarted = await AdaptiveLocationTracker.checkout();
checkout() returning true means the checkout flow was started successfully. Final result arrives later on eventsStream:
checkout_success: all pending points were flushed and the service stoppederrorwithCHECKOUT_FLUSH_FAILED: pending data could not be fully flushed, so the service stays active
4. Immediate Stop #
Stops the service immediately without flushing the database.
await AdaptiveLocationTracker.stop();
📡 Backend Implementation #
A. Tracking Endpoint (url) #
Receives an Array of location objects.
[
{
"latitude": 12.9716,
"longitude": 77.5946,
"timestamp": 1715065200000,
"accuracy": 15.2,
"user_id": 101
}
]
Important:
- The request body is a top-level JSON array, not a wrapped object like
{ "locations": [...] }. extraDatais flattened into each location object before sending.- Do not use reserved keys inside
extraData, because they can conflict with core fields:latitudelongitudetimestampaccuracy
B. Status Report Endpoint (reportLocationUrl) #
Receives a single status Object.
{
"latitude": 12.9716,
"longitude": 77.5946,
"remarks": "Location disabled by user"
}
⚙️ Core Logic (State Machine) #
- MOVING State: Records points if they are >10m apart and meet accuracy thresholds.
- STATIONARY State: The service "locks" to a clean point and ignores GPS drift until a significant move (30m+) is detected.
- Sync Scheduler: Runs every
intervalSeconds. It fetches pending data, sends a batch, and deletes them only after a200 OKresponse.
Event Codes #
Common error event codes emitted by the Android service:
LOCATION_DISABLEDLOCATION_PERMISSION_MISSINGLOCATION_REQUEST_FAILEDLOCATION_TEMPORARILY_UNAVAILABLELOCATION_SECURITY_ERRORSYNC_FAILUREWEBSOCKET_FAILURECHECKOUT_FLUSH_FAILED
Transport Reliability #
HTTP / REST mode #
- Batched locations are sent with HTTP
POST. - Local records are deleted only after the server returns a successful
2xxresponse. - This is the recommended mode when reliable delivery matters.
WebSocket mode #
- WebSocket mode is suitable for live, low-latency streaming.
- The current implementation does not use server ACK messages to confirm delivery before considering a batch sent.
- For that reason, WebSocket mode should be treated as best-effort live transport unless your backend and plugin are extended with explicit ACK semantics.
- If your use case requires strong delivery guarantees, prefer HTTP mode.
❤️ Credits #
Built for high-reliability field operations with advanced jitter filtering and offline resilience.