Production-grade background geolocation for Flutter — fully open-source.
Battery-conscious motion-detection intelligence, geofencing, SQLite persistence, HTTP sync, and headless Dart execution for iOS & Android.
- Motion-detection intelligence — Uses accelerometer, gyroscope & activity recognition to detect when the device is moving or stationary. Automatically toggles location services to conserve battery.
- Background location tracking — Continuous GPS recording with configurable
distanceFilteranddesiredAccuracy. Works after app is minimized, killed, or device rebooted. - Geofencing — Add circular or polygon geofences with enter/exit/dwell detection. Unlimited geofences via proximity-based auto-load/unload — only the closest geofences within
geofenceProximityRadiusare registered with the OS (up to 100 on Android, 20 on iOS), enabling monitoring of thousands of geofences despite platform limits. - SQLite persistence — All locations stored locally in SQLite. Query, count, delete, or sync to your server. Configurable retention (max days/records) and per-type persistence modes.
- HTTP auto-sync — Configurable batch upload with retry, exponential backoff, and offline queuing. Wi-Fi-only sync option via
disableAutoSyncOnCellular. ConfigurablemaxRetries,retryBackoffBase, andretryBackoffCapfor fine-tuned retry strategy. Connectivity-aware deferred sync on both platforms. 401-aware retry — on HTTP 401, fires a headless Dart callback to refresh JWT tokens viaregisterHeadlessHeadersCallback(), then retries with updated headers. - SSL certificate pinning — Pin server certificates or SHA-256 fingerprints to prevent MITM attacks. Configure via
HttpConfig.sslPinningCertificates(PEM) orHttpConfig.sslPinningFingerprints(hex SHA-256). Native validation on both platforms. - Dynamic HTTP headers — Set headers at runtime via
setDynamicHeaders(), or register a callback withsetHeadersCallback()for on-demand header generation (e.g., rotating auth tokens). Headers merge with staticHttpConfig.headers. Background-safe viaregisterHeadlessHeadersCallback(). - Route context — Attach arbitrary metadata (
routeId,driverId,vehicleId, etc.) to every synced location viasetRouteContext(). Context is included in the HTTP sync body and cleared withclearRouteContext(). - Custom sync body builder — Full control over the HTTP sync payload via
setSyncBodyBuilder(). Receives locations and route context, returns a customMaporList. Background-safe viaregisterHeadlessSyncBodyBuilder(). - Adaptive sampling — Automatically adjusts
distanceFilterbased on detected activity (walking, driving, still), battery level, and speed. Reduces GPS polling when stationary or battery-low, increases resolution when driving. Enable withenableAdaptiveMode: true. - Health check API — Single-call
getHealth()returns a comprehensive diagnostic snapshot: tracking state, permissions, battery, OEM health, sensors, database stats, and auto-detected warnings. Build monitoring dashboards or pre-flight checks with zero boilerplate. - Headless execution — Run Dart code in response to background events even when the Flutter UI is not running.
- Start on boot — Resume tracking automatically after device reboot.
- Scheduling — Define time-based schedules (e.g., "Mon–Fri 9AM–5PM"). Use
scheduleUseAlarmManageron Android for exact-time execution. - Comprehensive logging — SQLite-backed log system with email export.
- Debug sounds — Audible feedback during development for location, motion, geofence, and HTTP events.
- Elasticity control — Speed-based automatic distance filter scaling, with
disableElasticityandelasticityMultiplieroverrides. - Location filtering — Reject GPS spikes and low-accuracy readings with
LocationFilter(accuracy thresholds, max implied speed, odometer filtering). - Kalman filter GPS smoothing — Optional Extended Kalman Filter smooths GPS coordinates in real-time, eliminating jitter and producing cleaner tracks. Enable with
useKalmanFilter: trueinLocationFilter. - Trip detection — Automatic trip start/stop events based on motion state transitions. Each trip includes distance, duration, start/stop location, and all waypoints. Subscribe via
onTrip(). - Polygon geofences — Define geofences with arbitrary polygon vertices using ray-casting point-in-polygon containment checks. Set
verticeson aGeofenceinstead of using a circular radius. - Auto-stop — Automatically stop tracking after a configurable number of minutes via
stopAfterElapsedMinutes. - Configurable motion sensitivity — Tune accelerometer thresholds (
shakeThreshold,stillThreshold,stillSampleCount) from Dart. Use built-in presets (Low / Medium / High) or set custom values. iOS auto-converts m/s² to g-force. - Activity recognition tuning — Adjust confidence thresholds, stop-detection delays, and stationary behavior.
- Timestamp metadata — Optional extra timing fields on each location record via
enableTimestampMeta. - Geofence high-accuracy mode — Run the full GPS + motion pipeline in geofence-only mode (Android) via
geofenceModeHighAccuracy. - Prevent suspend (iOS) — Silent audio keep-alive to prevent iOS from suspending the app in the background.
- iOS background task protection — All critical native operations (location persist, HTTP sync, headless engine boot, lifecycle transitions) wrapped in
beginBackgroundTaskfor safe background execution. - iOS 17+ / 18+ session APIs —
CLBackgroundActivitySession(iOS 17+) andCLServiceSession(iOS 18+) for extended background runtime and authorization state. - Dart-controlled permissions — No native dialogs. Full Dart-side customization of permission UI, translations, and behavior.
- Foreground service toggle — Run with or without a persistent notification (Android).
- Shared Dart algorithms — Location filtering (elasticity, accuracy, speed), geofence proximity evaluation, schedule parsing, and persistence logic all run in shared Dart code for cross-platform consistency. Write once — works on Android, iOS, web, and future desktop platforms.
- Battery budget engine — Automatic feedback control loop adjusts
distanceFilter,desiredAccuracy, and periodic interval to stay within a configurable battery drain budget (batteryBudgetPerHour, typical 1.0–5.0 %/hr). Subscribe to real-time adjustment events viaonBudgetAdjustment(). - Carbon footprint estimator — Per-trip and cumulative CO₂ emission calculator using EU EEA 2024 mode-specific factors (gCO₂/km): car = 192, bus = 89, train = 41, walking/cycling = 0. Integrates with activity recognition to track distance per transport mode.
- Delta encoding — Batch location compression codec achieving 60–80% payload reduction for HTTP sync. First location transmitted in full; subsequent positions as deltas with shortened field names. Triple implementation (Dart + Kotlin + Swift) for native encoding.
- R-tree spatial index — O(log n) geofence proximity queries supporting 10,000+ geofences with sub-millisecond lookup.
queryCircle()andqueryBBox()APIs with Haversine-verified results. - GDPR/CCPA compliance reports —
generateComplianceReport()returns a structured data processing inventory covering: stored/synced counts, retention policy, privacy zones, encryption status, permissions, audit trail, and tracking config. Exports to JSON and Markdown. - Sparse updates — App-level location deduplication at the persistence layer. Drops locations within
sparseDistanceThreshold(default 50 m) of the last recorded position, with configurable idle heartbeat interval. - Dead reckoning — Inertial navigation using accelerometer + gyroscope + compass when GPS is lost for longer than
deadReckoningActivationDelayseconds. Auto-stops after configurable duration to prevent IMU drift. Query real-time state (active,elapsed,estimatedAccuracy) viagetDeadReckoningState(). - Database encryption — At-rest encryption for the local SQLite database. Android uses SQLCipher (AES-256) with keys stored in Android Keystore. iOS uses
NSFileProtectionCompletebacked by the Secure Enclave. Enable viaSecurityConfig(encryptDatabase: true)or encrypt at runtime withencryptDatabase(). - Device attestation — Verify device and app integrity using Google Play Integrity (Android) and App Attest (iOS). Generate cryptographic attestation tokens for server-side verification. Auto-refresh with configurable intervals.
- Wi-Fi-only sync —
disableAutoSyncOnCellularskips HTTP auto-sync on cellular networks, syncing only when connected to Wi-Fi. Supported on Android, iOS, and Web. - Periodic mode — Configurable one-shot location fixes at intervals from 60 seconds to 12 hours. Android supports sub-15-minute intervals via foreground service and exact alarms via
AlarmManager. - Live map view — Built-in example with OpenStreetMap tiles, speed-colored route trail, geofence visualization, trip overlay, and real-time status overlay.
Tracelet uses a federated plugin architecture with 4 packages:
| Package | Description |
|---|---|
tracelet |
App-facing Dart API — the only package you depend on |
tracelet_platform_interface |
Abstract platform interface + Pigeon definitions |
tracelet_android |
Kotlin Android implementation |
tracelet_ios |
Swift iOS implementation |
tracelet_web |
Web implementation (experimental) |
import 'package:tracelet/tracelet.dart' as tl;
// 1. Listen to events
tl.Tracelet.onLocation((tl.Location location) {
print('[location] $location');
});
tl.Tracelet.onMotionChange((tl.Location location) {
print('[motionchange] isMoving: ${location.isMoving}');
});
// 2. Configure & ready
final state = await tl.Tracelet.ready(tl.Config(
geo: tl.GeoConfig(
desiredAccuracy: tl.DesiredAccuracy.high,
distanceFilter: 10.0,
// Reject GPS spikes and low-accuracy readings
filter: tl.LocationFilter(
trackingAccuracyThreshold: 100,
maxImpliedSpeed: 80,
),
),
app: tl.AppConfig(
stopOnTerminate: false,
startOnBoot: true,
),
persistence: tl.PersistenceConfig(
maxDaysToPersist: 7,
maxRecordsToPersist: 5000,
),
logger: tl.LoggerConfig(
debug: true,
logLevel: tl.LogLevel.verbose,
),
));
// 3. Start tracking
if (!state.enabled) {
await tl.Tracelet.start();
}Tracelet does not show any native permission dialogs — only the OS prompt is triggered. All permission UI is controlled from Dart, giving you full freedom to customize dialogs, translations, and behavior.
| Method | Description |
|---|---|
Tracelet.getPermissionStatus() |
Read-only check — no dialog |
Tracelet.requestPermission() |
Triggers OS dialog, returns result |
Tracelet.getNotificationPermissionStatus() |
Notification status (Android 13+) |
Tracelet.requestNotificationPermission() |
Request notification permission (Android 13+) |
Tracelet.openAppSettings() |
Opens the app's system settings |
Tracelet.openLocationSettings() |
Opens device location settings |
Tracelet.openBatterySettings() |
Opens battery optimization (Android) |
Status codes: 0 notDetermined · 1 denied · 2 whenInUse · 3 always · 4 deniedForever
final status = await tl.Tracelet.getPermissionStatus();
if (status == 4) {
// Permanently denied — show YOUR Dart dialog with "Open Settings" button
await tl.Tracelet.openAppSettings();
return;
}
if (status == 0 || status == 1) {
final result = await tl.Tracelet.requestPermission(); // foreground
if (result == 2) {
// Show background rationale dialog, then:
await tl.Tracelet.requestPermission(); // upgrade to Always
}
} else if (status == 2) {
// Already foreground — show rationale, then upgrade
await tl.Tracelet.requestPermission();
}See the Permissions Guide for complete dialog implementations (denied dialog, background rationale dialog, notification rationale dialog, full escalation flow) with copy-paste Flutter code.
Android 13+: Request notification permission first, otherwise the notification is hidden. See Notification Permission.
iOS: Foreground service config is ignored — iOS uses its own background-mode mechanisms (BackgroundTasks, CoreLocation significant changes). No notification permission is needed for background location.
// Android 13+: ensure notification permission
if (Platform.isAndroid) {
final ns = await tl.Tracelet.getNotificationPermissionStatus();
if (ns != 3) await tl.Tracelet.requestNotificationPermission();
}
await tl.Tracelet.ready(tl.Config(
app: tl.AppConfig(
stopOnTerminate: false,
startOnBoot: true,
foregroundService: tl.ForegroundServiceConfig(
notificationTitle: 'My App',
notificationText: 'Tracking your location',
),
),
));
await tl.Tracelet.start();await tl.Tracelet.ready(tl.Config(
app: tl.AppConfig(
stopOnTerminate: true,
foregroundService: tl.ForegroundServiceConfig(enabled: false),
),
));
await tl.Tracelet.start();See the Background Tracking Guide for runtime switching with
setConfig().
| Guide | Description |
|---|---|
| Android Setup | Gradle, permissions, and manifest configuration |
| iOS Setup | Info.plist, capabilities, and entitlements |
| Permissions | Permission flow, status codes, Dart dialog examples |
| Background Tracking | Foreground service, silent mode, runtime switching |
| API Reference | All methods, events, and return types |
| Configuration | All config groups with property tables |
| Kalman Filter | GPS smoothing — how it works, when to use it |
| Trip Detection | Automatic trip events — setup, API, edge cases |
| Polygon Geofences | Polygon geofences — vertices, ray-casting, examples |
| Web Support | Web platform capabilities, limitations, and browser APIs |
| iOS Background Hardening | Background task protection, session APIs, prevent suspend |
| Adaptive Sampling | Multi-factor distance filter — activity, battery, speed |
| Health Check | Single-call diagnostics — warnings, permissions, sensors |
| HTTP Sync | Retry strategy, exponential backoff, connectivity handling |
| Privacy Zones | Location exclusion zones for sensitive areas |
| Audit Trail | Cryptographic hash-chain audit trail for compliance |
| Mock Detection | Detect spoofed locations, mock provider flags, trust scoring |
| OEM Compatibility | OEM-specific battery kill issues, manufacturer workarounds |
| Battery Budget | Feedback loop — auto-tune tracking to stay within battery drain target |
| Delta Encoding | 60–80% HTTP payload compression via differential location encoding |
| Carbon Estimator | Real-time CO₂ estimation by transport mode (EU EEA 2024 factors) |
| Compliance Report | Auto-generated GDPR Article 30 / CCPA compliance reports |
| Dead Reckoning | IMU-based inertial navigation during GPS signal loss |
| Database Encryption | At-rest SQLite encryption (SQLCipher / NSFileProtection) |
| Device Attestation | Platform attestation tokens (Play Integrity / App Attest) |
| Sparse Updates | App-level location deduplication — reduce DB writes and sync volume |
| Periodic Mode | Timed one-shot GPS fixes — WorkManager, foreground service, exact alarms |
| Platform | Minimum Version |
|---|---|
| Android | API 26 (Android 8.0 Oreo) |
| iOS | 14.0 |
| Web | Modern browsers (Chrome, Firefox, Safari, Edge) |
| Flutter | 3.22+ |
| Dart | 3.4+ |
See CONTRIBUTING.md for guidelines.
If you find Tracelet useful, consider buying me a coffee:
Apache 2.0 — see LICENSE for details.
All native code is written from scratch. No proprietary SDK dependencies.


