tracelet 3.2.17
tracelet: ^3.2.17 copied to clipboard
Production-grade background geolocation for Flutter. Battery-conscious tracking, geofencing, SQLite persistence, HTTP sync, and headless execution for iOS & Android.
example/main.dart
// ignore_for_file: avoid_print
import 'package:tracelet/tracelet.dart' as tl;
/// Minimal example demonstrating Tracelet background geolocation.
Future<void> main() async {
// 1. Subscribe to location events.
tl.Tracelet.onLocation((location) {
print(
'๐ ${location.coords.latitude}, ${location.coords.longitude} '
'ยท accuracy: ${location.coords.accuracy}m',
);
});
// 2. Initialize the plugin with a configuration.
final state = await tl.Tracelet.ready(
const tl.Config(logger: tl.LoggerConfig(logLevel: tl.LogLevel.verbose)),
);
print(
'Tracelet ready โ enabled: ${state.enabled}, '
'tracking: ${state.trackingMode}',
);
// 3. Start tracking.
await tl.Tracelet.start();
}
// ---------------------------------------------------------------------------
// One-Shot Location Examples
// ---------------------------------------------------------------------------
/// Example: Single location fetch โ no continuous tracking, no persistence.
///
/// This is the simplest way to get the device's current location without
/// starting background tracking or showing a foreground notification.
Future<void> singleLocationExample() async {
// Initialize with foreground service DISABLED (Android).
// No persistent notification will be shown.
await tl.Tracelet.ready(
const tl.Config(
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(
enabled: false, // No foreground notification on Android.
),
),
),
);
// Ensure location permission is granted before requesting.
final authStatus = await tl.Tracelet.requestLocationAuthorization();
if (authStatus != tl.AuthorizationStatus.whenInUse &&
authStatus != tl.AuthorizationStatus.always) {
print('โ ๏ธ Location permission denied (status=$authStatus)');
return;
}
// Fetch a single location โ does NOT start continuous tracking.
final location = await tl.Tracelet.getCurrentPosition(
desiredAccuracy: tl.DesiredAccuracy.high,
timeout: 30,
persist: false, // Don't store in local database.
);
print(
'๐ Single fix: ${location.coords.latitude}, '
'${location.coords.longitude} '
'accuracy: ${location.coords.accuracy}m',
);
}
/// Example: Best-of-N samples โ collect multiple GPS fixes and return the
/// one with the best (lowest) horizontal accuracy.
///
/// Useful for check-in flows, geocoding, or any scenario where you need
/// high confidence in a single reading.
Future<void> bestOfThreeSamplesExample() async {
await tl.Tracelet.ready(
const tl.Config(
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(enabled: false),
),
),
);
// Ensure location permission is granted before requesting.
final authStatus = await tl.Tracelet.requestLocationAuthorization();
if (authStatus != tl.AuthorizationStatus.whenInUse &&
authStatus != tl.AuthorizationStatus.always) {
print('โ ๏ธ Location permission denied (status=$authStatus)');
return;
}
// Collect 3 GPS samples, return the most accurate one.
final location = await tl.Tracelet.getCurrentPosition(
desiredAccuracy: tl.DesiredAccuracy.high,
timeout: 30,
samples: 3,
persist: false,
);
print(
'๐ Best of 3: ${location.coords.latitude}, '
'${location.coords.longitude} '
'accuracy: ${location.coords.accuracy}m',
);
}
/// Example: Last known location โ instant retrieval from OS cache.
///
/// No GPS hardware is activated. Returns null if the OS has no cached
/// location (e.g. fresh device boot with no prior location providers used).
Future<void> lastKnownLocationExample() async {
await tl.Tracelet.ready(
const tl.Config(
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(enabled: false),
),
),
);
// Ensure location permission is granted before requesting.
final authStatus = await tl.Tracelet.requestLocationAuthorization();
if (authStatus != tl.AuthorizationStatus.whenInUse &&
authStatus != tl.AuthorizationStatus.always) {
print('โ ๏ธ Location permission denied (status=$authStatus)');
return;
}
final location = await tl.Tracelet.getLastKnownLocation();
if (location == null) {
print('โ ๏ธ No cached location available');
} else {
print(
'๐ Last known: ${location.coords.latitude}, '
'${location.coords.longitude} '
'accuracy: ${location.coords.accuracy}m',
);
}
}
// ---------------------------------------------------------------------------
// Advanced Configuration Examples โ New Features
// ---------------------------------------------------------------------------
/// Example: Elasticity control โ disable speed-based distance filter scaling.
///
/// By default, Tracelet dynamically adjusts the distance filter based on
/// speed. Disable elasticity for a fixed `distanceFilter` regardless of speed.
Future<void> elasticityExample() async {
await tl.Tracelet.ready(
const tl.Config(
geo: tl.GeoConfig(
distanceFilter: 50,
// Disable elasticity: record at exactly every 50m regardless of speed.
disableElasticity: true,
),
),
);
await tl.Tracelet.start();
}
/// Example: Location filtering โ reject GPS spikes and low-accuracy readings.
///
/// The [tl.LocationFilter] denoises raw GPS samples before they are recorded.
/// This helps eliminate noise, phantom jumps, and low-quality readings.
Future<void> locationFilterExample() async {
await tl.Tracelet.ready(const tl.Config());
await tl.Tracelet.start();
}
/// Example: Auto-stop โ automatically stop tracking after N minutes.
///
/// Useful for time-boxed tracking sessions (e.g., a 30-minute workout).
Future<void> autoStopExample() async {
await tl.Tracelet.ready(
const tl.Config(
geo: tl.GeoConfig(
// Automatically stop tracking after 30 minutes. Use -1 to disable.
stopAfterElapsedMinutes: 30,
),
),
);
await tl.Tracelet.start();
print('Tracking will auto-stop after 30 minutes');
}
/// Example: Persistence control โ choose what to persist and retention limits.
///
/// Fine-tune what goes into the SQLite database and how long records are kept.
Future<void> persistenceConfigExample() async {
await tl.Tracelet.ready(
const tl.Config(
persistence: tl.PersistenceConfig(
// Auto-prune records older than 7 days. Use -1 for unlimited.
maxDaysToPersist: 7,
// Auto-prune when exceeding 5000 records. Use -1 for unlimited.
maxRecordsToPersist: 5000,
),
),
);
await tl.Tracelet.start();
}
/// Example: Geofence high-accuracy mode โ use continuous GPS in geofence-only mode.
///
/// By default, `startGeofences()` uses the platform's passive geofence monitoring
/// (battery friendly but less precise). Enable high-accuracy mode to run the
/// full GPS + motion pipeline even in geofence-only mode.
Future<void> geofenceHighAccuracyExample() async {
await tl.Tracelet.ready(
const tl.Config(
android: tl.AndroidConfig(
// Enable high-accuracy geofence monitoring (Android only).
geofenceModeHighAccuracy: true,
),
),
);
// Add a geofence first
await tl.Tracelet.addGeofence(
const tl.Geofence(
identifier: 'office',
latitude: 37.4220,
longitude: -122.0841,
radius: 200,
),
);
// Start geofence-only mode with high-accuracy GPS active.
await tl.Tracelet.startGeofences();
}
/// Example: Timestamp metadata โ append extra timing info to each location.
///
/// When enabled, each location record includes additional timestamp fields
/// useful for debugging timing issues and analyzing location pipeline latency.
Future<void> timestampMetaExample() async {
await tl.Tracelet.ready(
const tl.Config(geo: tl.GeoConfig(enableTimestampMeta: true)),
);
tl.Tracelet.onLocation((loc) {
print('๐ ${loc.coords.latitude}, ${loc.coords.longitude}');
// With enableTimestampMeta: true, the location record contains
// additional fields for arrival time at the platform layer.
});
await tl.Tracelet.start();
}
/// Example: Motion detection tuning โ adjust activity recognition sensitivity.
///
/// Fine-tune how aggressively Tracelet detects motion state changes.
Future<void> motionTuningExample() async {
await tl.Tracelet.ready(const tl.Config());
await tl.Tracelet.start();
}
/// Example: iOS prevent suspend โ keep the app alive in background.
///
/// Plays a silent audio clip to prevent iOS from suspending the app.
/// Uses minimal battery but ensures continuous background execution.
Future<void> preventSuspendExample() async {
await tl.Tracelet.ready(
const tl.Config(
app: tl.AppConfig(stopOnTerminate: false, startOnBoot: true),
ios: tl.IosConfig(
// iOS only: prevent iOS from suspending the app.
preventSuspend: true,
),
),
);
await tl.Tracelet.start();
}
/// Example: Android schedule with AlarmManager โ exact-time scheduling.
///
/// Uses AlarmManager for precise schedule execution instead of the default
/// JobScheduler/WorkManager which may defer execution.
Future<void> scheduleAlarmManagerExample() async {
await tl.Tracelet.ready(
const tl.Config(
app: tl.AppConfig(
stopOnTerminate: false,
startOnBoot: true,
// Define a schedule: Mon-Fri, 9am-5pm
schedule: ['1-5 09:00-17:00'],
),
android: tl.AndroidConfig(
// Android only: use AlarmManager for exact schedule timing.
scheduleUseAlarmManager: true,
),
),
);
// The schedule will auto-start/stop tracking at the defined times.
}
/// Example: Wi-Fi-only sync โ disable auto-sync on cellular connections.
///
/// Useful for bandwidth-conscious apps that should only sync on Wi-Fi.
Future<void> wifiOnlySyncExample() async {
await tl.Tracelet.ready(
const tl.Config(
http: tl.HttpConfig(
url: 'https://example.com/locations',
// Only sync when connected to Wi-Fi, not on cellular.
disableAutoSyncOnCellular: true,
),
),
);
await tl.Tracelet.start();
}
/// Example: Dart-side permission flow โ check, request, handle denial.
///
/// No native dialogs are shown. The OS permission prompt is the only native
/// UI. If permanently denied, guide the user to Settings from Dart.
Future<void> permissionFlowExample() async {
// 1. Check current status without triggering any dialog.
final status = await tl.Tracelet.getLocationAuthorization();
print('Current permission status: $status');
if (status == tl.AuthorizationStatus.notDetermined ||
status == tl.AuthorizationStatus.denied) {
// notDetermined or denied (can ask again) โ request foreground.
final result = await tl.Tracelet.requestLocationAuthorization();
print('After request: $result');
if (result == tl.AuthorizationStatus.deniedForever) {
// Permanently denied โ show YOUR OWN Dart dialog here, e.g.:
// showDialog(context, builder: (_) => AlertDialog(
// title: Text('Permission Required'),
// content: Text('Open Settings to enable location access.'),
// actions: [
// TextButton(onPressed: () => Tracelet.openAppSettings(), ...),
// ],
// ));
print('Permanently denied โ open settings');
await tl.Tracelet.openAppSettings();
return;
}
if (result == tl.AuthorizationStatus.whenInUse) {
// Foreground granted โ request background upgrade.
final bgResult = await tl.Tracelet.requestLocationAuthorization();
print('Background request: $bgResult');
}
} else if (status == tl.AuthorizationStatus.deniedForever) {
// Already permanently denied โ guide user to settings.
await tl.Tracelet.openAppSettings();
return;
}
// Permission is now whenInUse (2) or always (3) โ safe to start.
await tl.Tracelet.ready(
const tl.Config(
app: tl.AppConfig(stopOnTerminate: false, startOnBoot: true),
),
);
await tl.Tracelet.start();
}
/// Example: Full-featured config โ combining all new features.
///
/// Demonstrates how all configuration options work together.
Future<void> fullFeaturedExample() async {
tl.Tracelet.onLocation((loc) {
print(
'๐ ${loc.coords.latitude}, ${loc.coords.longitude} '
'acc=${loc.coords.accuracy}m',
);
});
await tl.Tracelet.ready(
const tl.Config(
geo: tl.GeoConfig(enableTimestampMeta: true),
app: tl.AppConfig(stopOnTerminate: false, startOnBoot: true),
http: tl.HttpConfig(
url: 'https://example.com/locations',
disableAutoSyncOnCellular: true,
),
persistence: tl.PersistenceConfig(
maxDaysToPersist: 7,
maxRecordsToPersist: 5000,
),
logger: tl.LoggerConfig(logLevel: tl.LogLevel.verbose, debug: true),
),
);
await tl.Tracelet.start();
}