### Initialize and Manage Geolocation Service (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This snippet covers the initialization, starting, stopping, and dynamic configuration of the GeolocationService using the flutter_background_geolocation library. It outlines how to set up background tracking, handle events, and manage service states. Dependencies include the 'flutter_background_geolocation' and 'traccar_client' packages. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/geolocation_service.dart'; import 'package:traccar_client/preferences.dart'; // Initialize geolocation service with user preferences await GeolocationService.init(); // Configures background tracking, event handlers, and headless task (Android) // Start continuous tracking await bg.BackgroundGeolocation.start(); // Begins background location updates based on configured intervals/filters // Returns: bg.State object with tracking status // Stop tracking await bg.BackgroundGeolocation.stop(); // Halts location updates and motion detection // Update configuration dynamically await bg.BackgroundGeolocation.setConfig(Preferences.geolocationConfig()); // Applies new settings without restarting tracking ``` -------------------------------- ### Flutter GPS Tracking Setup and Lifecycle Management Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code sets up a complete GPS tracking system within a Flutter application. It initializes necessary services like Firebase, preferences, geolocation, push notifications, and password protection. The code configures tracking parameters such as accuracy, distance filter, update intervals, and buffering. It also manages the background tracking service, enabling listeners for location updates, motion detection, and tracking state changes, and provides functionality to start, stop, and manually send location data. Dependencies include 'firebase_core', 'flutter_background_geolocation', and custom services for preferences, geolocation, push, configuration, and passwords. ```dart import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/preferences.dart'; import 'package:traccar_client/geolocation_service.dart'; import 'package:traccar_client/push_service.dart'; import 'package:traccar_client/configuration_service.dart'; import 'package:traccar_client/password_service.dart'; void main() async { // Step 1: Initialize Flutter and Firebase WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // Step 2: Initialize preferences with defaults await Preferences.init(); await Preferences.migrate(); // Step 3: Configure tracking parameters await Preferences.instance.setString(Preferences.id, 'FLEET-TRUCK-001'); await Preferences.instance.setString(Preferences.url, 'https://gps.company.com:5055'); await Preferences.instance.setString(Preferences.accuracy, 'high'); await Preferences.instance.setInt(Preferences.distance, 100); // 100 meters await Preferences.instance.setInt(Preferences.interval, 120); // 2 minutes await Preferences.instance.setInt(Preferences.fastestInterval, 30); // 30 seconds await Preferences.instance.setInt(Preferences.heartbeat, 300); // 5 minutes when stopped await Preferences.instance.setBool(Preferences.buffer, true); // Enable offline buffering await Preferences.instance.setBool(Preferences.wakelock, true); // Keep CPU awake (Android) await Preferences.instance.setBool(Preferences.stopDetection, true); // Enable motion detection // Step 4: Initialize geolocation with configuration await GeolocationService.init(); // Step 5: Initialize push notifications await PushService.init(); // Step 6: Set password protection (optional) await PasswordService.setPassword('SecurePass123'); // Step 7: Start tracking await bg.BackgroundGeolocation.start(); print('Tracking started'); runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { bool _isTracking = false; bg.Location? _lastLocation; String _statusMessage = 'Initializing...'; @override void initState() { super.initState(); _setupLocationListener(); _checkTrackingState(); } void _setupLocationListener() { // Listen for location updates bg.BackgroundGeolocation.onLocation((bg.Location location) { setState(() { _lastLocation = location; _statusMessage = 'Location updated: ${location.coords.latitude.toStringAsFixed(6)}, ${location.coords.longitude.toStringAsFixed(6)}'; }); print('New location: lat=${location.coords.latitude}, lon=${location.coords.longitude}, accuracy=${location.coords.accuracy}m'); }); // Listen for motion changes bg.BackgroundGeolocation.onMotionChange((bg.Location location) { setState(() { _statusMessage = location.isMoving ? 'Device is moving' : 'Device stopped'; }); print('Motion change: isMoving=${location.isMoving}'); }); // Listen for tracking state changes bg.BackgroundGeolocation.onEnabledChange((bool enabled) { setState(() { _isTracking = enabled; _statusMessage = enabled ? 'Tracking enabled' : 'Tracking disabled'; }); print('Tracking state: $enabled'); }); } Future _checkTrackingState() async { final state = await bg.BackgroundGeolocation.state; setState(() { _isTracking = state.enabled; }); } Future _toggleTracking() async { if (!await PasswordService.authenticate(context)) { return; // Password check failed } if (_isTracking) { await bg.BackgroundGeolocation.stop(); setState(() { _statusMessage = 'Tracking stopped'; }); } else { await bg.BackgroundGeolocation.start(); setState(() { _statusMessage = 'Tracking started'; }); } } Future _sendManualLocation() async { setState(() { _statusMessage = 'Requesting location...'; }); try { final location = await bg.BackgroundGeolocation.getCurrentPosition( samples: 1, persist: true, extras: {'manual': true}, ); setState(() { _lastLocation = location; _statusMessage = 'Manual location sent: ${location.coords.latitude.toStringAsFixed(6)}, ${location.coords.longitude.toStringAsFixed(6)}'; }); } catch (e) { setState(() { _statusMessage = 'Failed to get location: $e'; }); } } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Traccar Client Demo'), ), body: Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(_statusMessage), if (_lastLocation != null) Padding( padding: const EdgeInsets.only(top: 16.0), child: Text( 'Last Location: ${_lastLocation!.coords.latitude.toStringAsFixed(6)}, ${_lastLocation!.coords.longitude.toStringAsFixed(6)}', ), ), SizedBox(height: 20), ElevatedButton( onPressed: _toggleTracking, child: Text(_isTracking ? 'Stop Tracking' : 'Start Tracking'), ), ElevatedButton( onPressed: _sendManualLocation, child: Text('Send Manual Location'), ) ], ), ), ), ), ); } } ``` -------------------------------- ### Password Service Management (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code demonstrates how to set, remove, and authenticate passwords using the `PasswordService`. Passwords are stored securely, and authentication is required before sensitive actions like starting background tracking or opening settings. The `authenticate` method returns a boolean indicating success or failure. ```dart import 'package:flutter/material.dart'; import 'package:traccar_client/password_service.dart'; // Set a password (first time or change) await PasswordService.setPassword('MySecurePassword123'); // Stores securely in Keystore (Android) or Keychain (iOS) // Remove password protection await PasswordService.setPassword(''); // Deletes password from secure storage // Authenticate before sensitive action bool authenticated = await PasswordService.authenticate(context); if (authenticated) { // User entered correct password or no password set // Proceed with action (e.g., toggle tracking, open settings) await bg.BackgroundGeolocation.start(); } else { // Authentication failed // SnackBar shown: "Invalid password" } // Example: Protect tracking toggle Widget buildTrackingSwitch(BuildContext context) { return Switch( value: trackingEnabled, onChanged: (value) async { if (await PasswordService.authenticate(context)) { setState(() { trackingEnabled = value; if (value) { bg.BackgroundGeolocation.start(); } else { bg.BackgroundGeolocation.stop(); } }); } }, ); } // Example: Protect settings screen void openSettings(BuildContext context) async { if (await PasswordService.authenticate(context)) { Navigator.push( context, MaterialPageRoute(builder: (context) => SettingsScreen()), ); } } // Example: Change password in settings Widget buildPasswordField() { return TextField( controller: passwordController, obscureText: true, decoration: InputDecoration( labelText: 'Password (optional)', helperText: 'Leave empty to remove password protection', ), onSubmitted: (value) async { await PasswordService.setPassword(value); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Password updated')), ); }, ); } // Example: Factory reset via push notification // Server sends: {"data": {"command": "factoryReset"}} // Result: await PasswordService.setPassword(''); // Password cleared, app resets to defaults ``` -------------------------------- ### Apply Configuration via URI in Dart Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code demonstrates how to use the ConfigurationService to apply settings from various URI types. It shows how to parse HTTP/HTTPS URLs and the custom `traccar://` scheme with query parameters. The configuration is applied immediately to the application preferences. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/configuration_service.dart'; // Example 1: HTTP/HTTPS URL (extracts server address) final uri1 = Uri.parse('https://demo.traccar.org:5055'); await ConfigurationService.applyUri(uri1); // Sets: url = "https://demo.traccar.org:5055" final uri2 = Uri.parse('http://192.168.1.100:8082/traccar'); await ConfigurationService.applyUri(uri2); // Sets: url = "http://192.168.1.100:8082/traccar" // Example 2: traccar:// scheme with query parameters final uri3 = Uri.parse('traccar://demo.traccar.org:5055?id=DEVICE123&accuracy=highest&distance=0&interval=60'); await ConfigurationService.applyUri(uri3); // Sets: // - url = "traccar://demo.traccar.org:5055" (extracted from host+path) // - id = "DEVICE123" // - accuracy = "highest" // - distance = 0 // - interval = 60 // Example 3: Comprehensive configuration URI final uri4 = Uri.parse( 'traccar://server.example.com:5055?' 'id=FLEET001&' 'url=https://tracking.company.com:5055&' 'accuracy=high&' 'distance=100&' 'interval=120&' 'angle=45&' 'heartbeat=300&' 'fastestInterval=60&' 'buffer=true&' 'wakelock=false&' 'stopDetection=true' ); await ConfigurationService.applyUri(uri4); // Sets all parameters from query string // Example 4: QR code provisioning // Scan QR code containing: "traccar://demo.traccar.org?id=QR12345" // Result: Automatically applies configuration via ConfigurationService // Example 5: App Links deep linking // User clicks link: https://tracking.myapp.com/configure?id=MOBILE789 // App intercepts link and applies configuration // Example 6: Boolean parameter handling final uri5 = Uri.parse('traccar://server?buffer=false&wakelock=true&stopDetection=false'); await ConfigurationService.applyUri(uri5); // Sets: // - buffer = false (disable offline buffering) // - wakelock = true (enable Android wake lock) // - stopDetection = false (disable motion detection) // Example 7: Integer parameter handling final uri6 = Uri.parse('traccar://server?distance=50&interval=180&angle=30&heartbeat=600'); await ConfigurationService.applyUri(uri6); // Sets: // - distance = 50 (meters) // - interval = 180 (seconds) // - angle = 30 (degrees) // - heartbeat = 600 (seconds, 10 minutes) // Example 8: Invalid parameters ignored final uri7 = Uri.parse('traccar://server?distance=invalid&accuracy=unknown'); await ConfigurationService.applyUri(uri7); // Invalid values skipped, existing settings preserved // After applying configuration, geolocation config is updated await bg.BackgroundGeolocation.setConfig(Preferences.geolocationConfig()); // New settings take effect immediately without restart ``` -------------------------------- ### Initialize Traccar Client App with Firebase Services Source: https://context7.com/traccar/traccar-client/llms.txt This Dart snippet shows the main entry point for the Traccar Client application. It initializes Firebase services (Analytics, Crashlytics, Cloud Messaging), loads user preferences, and configures the geolocation and push notification services before launching the UI. It also handles deep linking and external configuration provisioning. ```dart import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/material.dart'; import 'package:traccar_client/geolocation_service.dart'; import 'package:traccar_client/push_service.dart'; import 'package:traccar_client/preferences.dart'; void main() async { // Initialize Flutter framework bindings WidgetsFlutterBinding.ensureInitialized(); // Initialize Firebase for analytics and crash reporting await Firebase.initializeApp(); FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; // Load persisted preferences with defaults await Preferences.init(); await Preferences.migrate(); // Configure background geolocation service await GeolocationService.init(); // Setup push notifications for remote commands await PushService.init(); // Launch the application runApp(const MainApp()); } ``` -------------------------------- ### Traccar Client Demo UI (Dart/Flutter) Source: https://context7.com/traccar/traccar-client/llms.txt This is the main build method for the Traccar Client Demo application's UI. It sets up a MaterialApp with a Scaffold, AppBar, and a Column layout for various widgets. It displays status information, last known location, and control buttons for tracking, manual location sending, and configuration updates. ```dart @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Traccar Client Demo'), backgroundColor: Colors.green, ), body: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Card( child: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Status', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text('Tracking: ${_isTracking ? "Enabled" : "Disabled"}'), Text('Device ID: ${Preferences.instance.getString(Preferences.id)}'), Text('Server: ${Preferences.instance.getString(Preferences.url)}'), SizedBox(height: 8), Text(_statusMessage, style: TextStyle(color: Colors.blue)), ], ), ), ), SizedBox(height: 16), if (_lastLocation != null) Card( child: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Last Location', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text('Latitude: ${_lastLocation!.coords.latitude.toStringAsFixed(6)}'), Text('Longitude: ${_lastLocation!.coords.longitude.toStringAsFixed(6)}'), Text('Accuracy: ${_lastLocation!.coords.accuracy.toStringAsFixed(1)} m'), Text('Speed: ${(_lastLocation!.coords.speed * 3.6).toStringAsFixed(1)} km/h'), Text('Heading: ${_lastLocation!.coords.heading.toStringAsFixed(0)}°'), Text('Altitude: ${_lastLocation!.coords.altitude.toStringAsFixed(1)} m'), Text('Moving: ${_lastLocation!.isMoving ? "Yes" : "No"}'), Text('Timestamp: ${_lastLocation!.timestamp}'), ], ), ), ), SizedBox(height: 16), ElevatedButton( onPressed: _toggleTracking, child: Text(_isTracking ? 'Stop Tracking' : 'Start Tracking'), style: ElevatedButton.styleFrom( backgroundColor: _isTracking ? Colors.red : Colors.green, padding: EdgeInsets.all(16), ), ), SizedBox(height: 8), ElevatedButton( onPressed: _sendManualLocation, child: Text('Send Location Now'), ), SizedBox(height: 8), ElevatedButton( onPressed: _updateConfiguration, child: Text('Switch to Highest Accuracy'), ), SizedBox(height: 8), ElevatedButton( onPressed: _applyQRConfiguration, child: Text('Apply QR Configuration'), ), ], ), ), ), ); } ``` -------------------------------- ### Apply QR Configuration (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This function simulates scanning a QR code containing Traccar client configuration and applies it. It parses a URI with server details and tracking parameters, then uses a ConfigurationService to apply these settings. The UI status message is updated upon successful application. ```dart Future _applyQRConfiguration() async { // Simulate QR code scan result final uri = Uri.parse('traccar://gps.mycompany.com:5055?id=VEHICLE789&accuracy=high&distance=50&interval=60'); await ConfigurationService.applyUri(uri); setState(() { _statusMessage = 'QR configuration applied'; }); } ``` -------------------------------- ### Traccar Client Push Notification Service Initialization and Command Handling (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt Initializes the push notification service, handles FCM token registration, refresh, and uploads to the server. Demonstrates processing of remote commands like requesting single positions, starting/stopping tracking, and factory resets via Firebase Cloud Messaging payloads. ```dart import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/push_service.dart'; // Initialize push notification service await PushService.init(); // Requests notification permissions // Registers background message handler // Sets up token refresh listener // Uploads FCM token to server when tracking enabled // Firebase message payload structure for remote commands // { // "data": { // "command": "positionSingle" // or "positionPeriodic", "positionStop", "factoryReset" // } // } // Command 1: Request single location (positionSingle) // Server sends: {"data": {"command": "positionSingle"}} // Result: Executes bg.BackgroundGeolocation.getCurrentPosition() // with extras: {'remote': true} // Command 2: Start continuous tracking (positionPeriodic) // Server sends: {"data": {"command": "positionPeriodic"}} // Result: Executes bg.BackgroundGeolocation.start() // Command 3: Stop tracking (positionStop) // Server sends: {"data": {"command": "positionStop"}} // Result: Executes bg.BackgroundGeolocation.stop() // Command 4: Factory reset (factoryReset) // Server sends: {"data": {"command": "factoryReset"}} // Result: Clears password and resets to defaults // FCM token upload to server // Automatically happens when: // 1. App initializes (if tracking enabled) // 2. Token refreshes // 3. Tracking is re-enabled after being stopped // Token upload HTTP request format: // POST https://demo.traccar.org:5055/ // Content-Type: application/x-www-form-urlencoded // Body: id=12345678¬ificationToken=FCM_TOKEN_HERE // Example: Manually trigger token upload final fcmToken = await FirebaseMessaging.instance.getToken(); print('FCM Token: $fcmToken'); // Output: "FCM Token: dXpQYzR2M3h5Wk1hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk..." // Example: Listen for foreground messages FirebaseMessaging.onMessage.listen((RemoteMessage message) { print('Received command: ${message.data['command']}'); // Automatically handled by PushService._onMessage() }); // Example: Background message handling // Background handler runs in separate isolate when app terminated @pragma('vm:entry-point') Future pushServiceBackgroundHandler(RemoteMessage message) async { await Preferences.init(); // Re-initialize in background isolate // Process command even when app not running } // Example: Token refresh handling FirebaseMessaging.instance.onTokenRefresh.listen((String newToken) { print('Token refreshed: $newToken'); // Automatically uploaded to server by PushService }); ``` -------------------------------- ### Offline Buffering and Synchronization Source: https://context7.com/traccar/traccar-client/llms.txt Describes the behavior of the Traccar client when the server is unreachable, including local storage of location data and subsequent synchronization. ```APIDOC ## Offline Buffering and Synchronization ### Description When the Traccar server is unreachable, the client can buffer location data locally if configured (e.g., `buffer=true`, `maxRecordsToPersist=-1`). The client periodically attempts to synchronize this buffered data with the server using `bg.BackgroundGeolocation.sync()`. Once the connection is restored, all buffered locations are sent in chronological order. ### Behavior 1. **Server Unreachable**: If the server cannot be reached, location updates are stored locally if buffering is enabled. 2. **Periodic Sync Attempts**: The client attempts to sync buffered data with the server at regular intervals. 3. **Connection Restored**: Upon successful connection, all stored location data is transmitted to the server. 4. **Chronological Order**: Buffered locations are sent in the order they were recorded. ### Synchronization Request Example When synchronizing multiple buffered locations, the POST request body may contain an array of location objects: ```json [ { "timestamp": "2025-10-25T14:00:00.000Z", "coords": { ... }, ... }, { "timestamp": "2025-10-25T14:05:00.000Z", "coords": { ... }, ... }, { "timestamp": "2025-10-25T14:10:00.000Z", "coords": { ... }, ... } ] ``` ``` -------------------------------- ### Manage Traccar Client Preferences with SharedPreferences Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code demonstrates how to manage application preferences using SharedPreferencesWithCache in the Traccar Client. It covers initialization, accessing various preference types (String, int, bool) with default values, and updating these settings. Preferences include device ID, server URL, location accuracy, update intervals, and tracking options. ```dart import 'package:shared_preferences/shared_preferences.dart'; // Initialize preferences with caching for performance await Preferences.init(); // Access preference values final deviceId = Preferences.instance.getString(Preferences.id); // Returns: "12345678" (random 8-digit number if not set) final serverUrl = Preferences.instance.getString(Preferences.url); // Returns: "http://demo.traccar.org:5055" (default) final accuracy = Preferences.instance.getString(Preferences.accuracy); // Returns: "medium" (options: "highest", "high", "medium", "low") final updateInterval = Preferences.instance.getInt(Preferences.interval); // Returns: 300 (seconds, default 5 minutes) final distanceFilter = Preferences.instance.getInt(Preferences.distance); // Returns: 75 (meters minimum distance to trigger update) final angleFilter = Preferences.instance.getInt(Preferences.angle); // Returns: 0 (degrees heading change for highest accuracy mode) final heartbeatInterval = Preferences.instance.getInt(Preferences.heartbeat); // Returns: 0 (disabled by default, minimum 60 seconds if enabled) final bufferingEnabled = Preferences.instance.getBool(Preferences.buffer); // Returns: true (stores locations when offline) final wakelockEnabled = Preferences.instance.getBool(Preferences.wakelock); // Returns: false (Android CPU wake lock during movement) final stopDetection = Preferences.instance.getBool(Preferences.stopDetection); // Returns: true (enables motion detection) // Update preference values await Preferences.instance.setString(Preferences.id, "87654321"); await Preferences.instance.setString(Preferences.url, "https://myserver.com:5055"); await Preferences.instance.setString(Preferences.accuracy, "highest"); await Preferences.instance.setInt(Preferences.interval, 60); // 1 minute await Preferences.instance.setInt(Preferences.distance, 50); // 50 meters await Preferences.instance.setInt(Preferences.angle, 30); // 30 degrees await Preferences.instance.setInt(Preferences.heartbeat, 300); // 5 minutes await Preferences.instance.setBool(Preferences.buffer, false); await Preferences.instance.setBool(Preferences.wakelock, true); await Preferences.instance.setBool(Preferences.stopDetection, false); ``` -------------------------------- ### Configure Background Geolocation Settings (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code demonstrates the creation of a `bg.Config` object for background geolocation tracking using the `flutter_background_geolocation` plugin. It showcases various configuration options for different accuracy levels, including highest (navigation), medium, and low, as well as settings for server communication, filtering, buffering, logging, and platform-specific adjustments for iOS and Android. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/preferences.dart'; // Generate configuration from current preferences final config = Preferences.geolocationConfig(); // Example configuration for HIGHEST accuracy mode: bg.Config config = bg.Config( isMoving: true, // Start tracking in moving state enableHeadless: true, // Run in background isolate (Android) stopOnTerminate: false, // Continue tracking if app killed startOnBoot: true, // Resume tracking after device restart // Accuracy mode determines GPS precision vs battery trade-off desiredAccuracy: bg.Config.DESIRED_ACCURACY_NAVIGATION, // iOS highest // Android uses: bg.Config.DESIRED_ACCURACY_HIGH // Options: NAVIGATION (iOS only), HIGH, MEDIUM, LOW autoSync: false, // Manual sync control via GeolocationService // Server configuration url: "https://demo.traccar.org:5055/", // Trailing slash added if missing params: { 'device_id': '12345678', // Unique device identifier }, // Filtering thresholds distanceFilter: 0.0, // 0 = no distance filter (highest accuracy) // Medium/High/Low accuracy: 75 meters default locationUpdateInterval: 0, // 0 = continuous updates (highest accuracy) // Medium/High/Low: 300000ms (5 minutes) default fastestLocationUpdateInterval: 0, // 0 = no minimum (highest accuracy) // Medium/High/Low: 30000ms (30 seconds) default heartbeatInterval: null, // Disabled by default // If enabled: sends location every N seconds when stationary (min 60) // Buffering configuration maxRecordsToPersist: -1, // Unlimited offline buffering // If buffer disabled: 1 (only current location) // Logging logLevel: bg.Config.LOG_LEVEL_VERBOSE, // DEBUG, VERBOSE, INFO, WARN, ERROR logMaxDays: 1, // Keep logs for 1 day // Location data template (JSON sent to server) locationTemplate: '{"timestamp":"<%= timestamp %>","coords":{"latitude":<%= latitude %>,"longitude":<%= longitude %>,"accuracy":<%= accuracy %>,"speed":<%= speed %>,"heading":<%= heading %>,"altitude":<%= altitude %>},"is_moving":<%= is_moving %>,"odometer":<%= odometer %>,"event":"<%= event %>","battery":{"level":<%= battery.level %>,"is_charging":<%= battery.is_charging %>},"activity":{"type":"<%= activity.type %>"},"extras":{},"_":"&id=12345678&lat=<%= latitude %>&lon=<%= longitude %>×tamp=<%= timestamp %>&"}', // Power management preventSuspend: false, // true if heartbeat enabled disableElasticity: true, // No adaptive location interval disableStopDetection: false, // Enable motion detection // iOS-specific settings pausesLocationUpdatesAutomatically: false, // Don't pause in highest accuracy // true for Medium/High/Low accuracy with stop detection enabled // Android-specific notification notification: bg.Notification( smallIcon: 'drawable/ic_stat_notify', priority: bg.Config.NOTIFICATION_PRIORITY_LOW, // Minimize distraction ), // Permission rationale dialog (Android) backgroundPermissionRationale: bg.PermissionRationale( title: 'Allow Traccar Client to access this device\'s location in the background', message: 'For reliable tracking, please enable \"Allow all the time\" location access.', positiveAction: 'Change to \"Allow all the time\"', negativeAction: 'Cancel' ), showsBackgroundLocationIndicator: false, // iOS status bar indicator ); // Example: MEDIUM accuracy configuration (default) bg.Config mediumConfig = bg.Config( desiredAccuracy: bg.Config.DESIRED_ACCURACY_MEDIUM, distanceFilter: 75.0, // 75 meters minimum distance locationUpdateInterval: 300000, // 5 minutes fastestLocationUpdateInterval: 30000, // 30 seconds minimum pausesLocationUpdatesAutomatically: true, // Pause when stopped (iOS) // ... other settings same as above ); // Example: LOW accuracy configuration (battery-efficient) bg.Config lowConfig = bg.Config( desiredAccuracy: bg.Config.DESIRED_ACCURACY_LOW, distanceFilter: 75.0, locationUpdateInterval: 300000, fastestLocationUpdateInterval: 30000, // ... other settings same as above ); ``` -------------------------------- ### Request Single Location Updates (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt Demonstrates how to request single location updates for various scenarios, including manual requests, stationary device heartbeats, and remote commands triggered by push notifications. Each request uses `getCurrentPosition` with specific `extras` to differentiate the purpose. These updates are persisted for synchronization. Dependencies include the 'flutter_background_geolocation' package. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; // Request single location update (manual) await bg.BackgroundGeolocation.getCurrentPosition( samples: 1, // Number of location samples to collect persist: true, // Save location to database for syncing extras: { // Additional metadata 'manual': true, // User-initiated request } ); // Returns: bg.Location object with coordinates, accuracy, speed, etc. // Request single location for heartbeat (stationary device) await bg.BackgroundGeolocation.getCurrentPosition( samples: 1, persist: true, extras: {'heartbeat': true} // Marks as heartbeat update ); // Request location via push notification (remote command) await bg.BackgroundGeolocation.getCurrentPosition( samples: 1, persist: true, extras: {'remote': true} // Server-initiated request ); ``` -------------------------------- ### Traccar Client Location Cache Operations (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt Demonstrates how to use the LocationCache to retrieve, store, and calculate differences between location updates. It handles in-memory and persistent storage via SharedPreferences. Dependencies include the `flutter_background_geolocation` package. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/location_cache.dart'; // Retrieve cached location final lastLocation = LocationCache.get(); if (lastLocation != null) { print('Timestamp: ${lastLocation.timestamp}'); // "2025-10-25T12:30:00.000Z" print('Latitude: ${lastLocation.latitude}'); // 37.7749 print('Longitude: ${lastLocation.longitude}'); // -122.4194 print('Heading: ${lastLocation.heading}'); // 180.0 (degrees) } // Returns null if no location cached yet // Store new location bg.Location newLocation = bg.Location( timestamp: '2025-10-25T12:35:00.000Z', coords: bg.Coords( latitude: 37.7758, longitude: -122.4180, accuracy: 8.5, speed: 12.5, heading: 185.0, altitude: 18.0, ), isMoving: true, ); await LocationCache.set(newLocation); // Persists to SharedPreferences: // - lastTimestamp: "2025-10-25T12:35:00.000Z" // - lastLatitude: 37.7758 // - lastLongitude: -122.4180 // - lastHeading: 185.0 // Calculate distance between cached location and new location final cachedLoc = LocationCache.get(); if (cachedLoc != null) { final distance = _distance(cachedLoc, newLocation); // Assuming _distance is defined elsewhere print('Distance traveled: ${distance.toStringAsFixed(2)} meters'); // Output: "Distance traveled: 152.34 meters" } // Calculate time elapsed since cached location if (cachedLoc != null) { final elapsed = DateTime.parse(newLocation.timestamp) .difference(DateTime.parse(cachedLoc.timestamp)) .inSeconds; print('Time elapsed: $elapsed seconds'); // Output: "Time elapsed: 300 seconds" } // Check heading change since cached location if (cachedLoc != null && cachedLoc.heading >= 0 && newLocation.coords.heading > 0) { final headingChange = (newLocation.coords.heading - cachedLoc.heading).abs(); print('Heading changed: ${headingChange.toStringAsFixed(1)} degrees'); // Output: "Heading changed: 5.0 degrees" } ``` -------------------------------- ### Synchronize Buffered Locations (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This code snippet shows how to synchronize all pending location updates that have been buffered by the GeolocationService to the server. It utilizes the `sync` method provided by the `flutter_background_geolocation` library. This is crucial for ensuring data consistency between the client and the server. Dependencies include the 'flutter_background_geolocation' package. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; // Synchronize buffered locations to server await bg.BackgroundGeolocation.sync(); // Sends all pending location updates via HTTP POST ``` -------------------------------- ### Traccar Client Location Data Transmission (Dart) Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code snippet illustrates the structure of location data sent via HTTP POST to a Traccar server. It includes details on timestamp, coordinates, movement status, battery level, activity, and custom extras. The `_` field is a legacy parameter for compatibility. ```dart // Server endpoint configuration final serverUrl = Preferences.instance.getString(Preferences.url); // Example: "https://demo.traccar.org:5055/" // HTTP POST request format // POST https://demo.traccar.org:5055/ // Content-Type: application/json // Authorization: (none required for default Traccar) // Location JSON payload structure { "timestamp": "2025-10-25T14:30:45.123Z", "coords": { "latitude": 37.7749, "longitude": -122.4194, "accuracy": 10.5, // meters "speed": 5.5, // meters per second (multiply by 3.6 for km/h) "heading": 180.0, // degrees (0-360, 0=North, 90=East, 180=South, 270=West) "altitude": 15.0 // meters above sea level }, "is_moving": true, "odometer": 12345.67, // cumulative distance in meters "event": "motionchange", // or "location", "heartbeat", "geofence", etc. "battery": { "level": 0.85, // 0.0-1.0 (85%) "is_charging": false }, "activity": { "type": "in_vehicle" // Activity recognition result // Other types: "on_bicycle", "on_foot", "running", "still", "walking", "unknown" }, "extras": {}, // Empty if no special metadata "_": "&id=12345678&lat=37.7749&lon=-122.4194×tamp=2025-10-25T14:30:45.123Z&" } // Location with manual request metadata { "timestamp": "2025-10-25T14:35:12.456Z", "coords": { /* ... */ }, "extras": { "manual": true // User clicked "Send Location" button }, "_": "&id=12345678&lat=37.7750&lon=-122.4195×tamp=2025-10-25T14:35:12.456Z&" } // Location with remote request metadata (from push notification) { "timestamp": "2025-10-25T14:40:00.789Z", "coords": { /* ... */ }, "extras": { "remote": true // Server sent "positionSingle" command }, "_": "&id=12345678&lat=37.7751&lon=-122.4196×tamp=2025-10-25T14:40:00.789Z&" } // Location with heartbeat metadata (stationary device) { "timestamp": "2025-10-25T14:45:00.000Z", "coords": { /* ... */ }, "is_moving": false, "extras": { "heartbeat": true // Sent every heartbeatInterval seconds when stationary }, "_": "&id=12345678&lat=37.7749&lon=-122.4194×tamp=2025-10-25T14:45:00.000Z&" } // Location with SOS alarm (emergency quick action) { "timestamp": "2025-10-25T14:50:00.000Z", "coords": { /* ... */ }, "extras": { "alarm": "sos" // Emergency signal from quick action }, "_": "&id=12345678&lat=37.7752&lon=-122.4197×tamp=2025-10-25T14:50:00.000Z&" } // Push notification token registration // POST https://demo.traccar.org:5055/ // Content-Type: application/x-www-form-urlencoded // Body: id=12345678¬ificationToken=FCM_TOKEN_STRING // Example: cURL request for manual location update curl -X POST https://demo.traccar.org:5055/ \ -H "Content-Type: application/json" \ -d '{ "timestamp": "2025-10-25T15:00:00.000Z", "coords": { "latitude": 37.7749, "longitude": -122.4194, "accuracy": 10.0, "speed": 0.0, "heading": 0.0, "altitude": 0.0 }, "is_moving": false, "odometer": 0.0, "event": "location", "battery": { "level": 1.0, "is_charging": true }, "activity": { "type": "still" }, "extras": { "manual": true }, "_": "&id=12345678&lat=37.7749&lon=-122.4194×tamp=2025-10-25T15:00:00.000Z&" }' // Traccar server response (typical) // HTTP 200 OK // Body: (empty or acknowledgment) // Offline buffering behavior // If server unreachable: // 1. Location stored locally (if buffer=true, maxRecordsToPersist=-1) // 2. Periodic retry attempts via bg.BackgroundGeolocation.sync() // 3. All buffered locations sent when connection restored // 4. Locations sent in chronological order // Example: Multiple buffered locations sent together // POST request body contains array of location objects // [ // { "timestamp": "2025-10-25T14:00:00.000Z", ... }, // { "timestamp": "2025-10-25T14:05:00.000Z", ... }, // { "timestamp": "2025-10-25T14:10:00.000Z", ... } // ] ``` -------------------------------- ### Register and Implement Headless Task (Android) Source: https://context7.com/traccar/traccar-client/llms.txt This Dart code snippet demonstrates how to register a headless task for background location processing on Android using the flutter_background_geolocation plugin. It includes the headless task entry point which handles various background events like location changes, motion changes, and heartbeat updates, ensuring that essential services like preferences are re-initialized within the background isolate. ```dart import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; import 'package:traccar_client/geolocation_service.dart'; import 'package:traccar_client/preferences.dart'; // Register headless task during initialization (main.dart) if (Platform.isAndroid) { await bg.BackgroundGeolocation.registerHeadlessTask(headlessTask); } // Headless task entry point (separate isolate) @pragma('vm:entry-point') // Prevents tree-shaking in release builds void headlessTask(bg.HeadlessEvent headlessEvent) async { // Re-initialize preferences in background isolate await Preferences.init(); // Process events based on type switch (headlessEvent.name) { case bg.Event.ENABLEDCHANGE: // Tracking enabled/disabled await GeolocationService.onEnabledChange(headlessEvent.event); break; case bg.Event.MOTIONCHANGE: // Device started/stopped moving await GeolocationService.onMotionChange(headlessEvent.event); break; case bg.Event.HEARTBEAT: // Heartbeat timer fired (stationary device) await GeolocationService.onHeartbeat(headlessEvent.event); break; case bg.Event.LOCATION: // New location received await GeolocationService.onLocation(headlessEvent.event); break; } } ``` -------------------------------- ### JSON Location Data Format Source: https://context7.com/traccar/traccar-client/llms.txt Illustrates the JSON format for server reception of location data, including coordinates, timestamp, movement status, battery information, and optional extras. This format is crucial for Traccar server to accurately process and store device location. ```json { "timestamp": "2025-10-25T14:30:00Z", "coords": { "latitude": 37.7749, "longitude": -122.4194, "accuracy": 10.5, "speed": 5.5, "heading": 180.0, "altitude": 15.0 }, "is_moving": true, "odometer": 12345.67, "event": "location", "battery": { "level": 0.85, "is_charging": false }, "activity": { "type": "in_vehicle" }, "extras": {}, "_": "&id=FLEET-TRUCK-001&lat=37.7749&lon=-122.4194×tamp=2025-10-25T14:30:00Z&" } ``` -------------------------------- ### POST / HTTP - Push Notification Token Registration Source: https://context7.com/traccar/traccar-client/llms.txt Registers the device's push notification token with the Traccar server. This allows the server to send remote commands or notifications to the device. ```APIDOC ## POST / HTTP - Push Notification Token Registration ### Description Registers the device's push notification token with the Traccar server. This is necessary for the server to send remote commands (e.g., 'positionSingle') or notifications to the device. ### Method POST ### Endpoint `[SERVER_URL]` (e.g., `https://demo.traccar.org:5055/`) ### Parameters #### Query Parameters None #### Request Body The request body is in `application/x-www-form-urlencoded` format and must contain: - **id** (string) - The unique device identifier. - **notificationToken** (string) - The FCM (Firebase Cloud Messaging) token or equivalent for the device's platform. ### Request Example ``` id=12345678¬ificationToken=FCM_TOKEN_STRING ``` ### Response #### Success Response (200) Typically an empty body or a simple acknowledgment confirming the token registration. #### Response Example (Empty Body) ```