trackiva 0.0.3 copy "trackiva: ^0.0.3" to clipboard
trackiva: ^0.0.3 copied to clipboard

A comprehensive Flutter plugin for location tracking in foreground, background, and terminated states with MQTT, GraphQL, and HTTP support.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:trackiva/trackiva.dart';
import 'package:trackiva/trackiva_platform_interface.dart';
import 'dart:math';
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _trackiva = Trackiva();

  // State variables
  String _platformVersion = 'Unknown';
  String _status = 'Not configured';
  bool _isConfigured = false;
  bool _isTracking = false;
  TrackivaState? _currentState;

  // Live location tracking
  LocationData? _currentLocation;
  final List<LocationData> _locationHistory = [];
  double _totalDistance = 0.0;
  double _averageSpeed = 0.0;
  DateTime? _lastLocationTime;
  int _locationCount = 0;

  // Event logs
  final List<String> _locationLogs = [];
  final List<String> _motionLogs = [];
  final List<String> _providerLogs = [];
  final List<String> _activityLogs = [];
  final List<String> _geofenceLogs = [];
  final List<String> _heartbeatLogs = [];
  final List<String> _httpLogs = [];
  final List<String> _connectivityLogs = [];
  final List<String> _powerSaveLogs = [];
  final List<String> _actionLogs = [];

  // Configuration
  TrackivaConfig _config = TrackivaConfig();

  @override
  void initState() {
    super.initState();
    _requestPermissions().then((_) {
      _initPlatformState();
      _setupEventListeners();
    });
  }

  Future<void> _requestPermissions() async {
    _addLog(_actionLogs, '[Permissions] Requesting location and notification permissions...');

    // Request location permissions
    Map<Permission, PermissionStatus> statuses = await [
      Permission.location,
      Permission.locationWhenInUse,
      Permission.locationAlways,
      Permission.notification,
    ].request();

    // Log permission results
    for (var permission in statuses.keys) {
      var status = statuses[permission];
      _addLog(_actionLogs, '[Permissions] ${permission.toString()}: $status');
    }

    // Check if we have the necessary permissions
    bool hasLocationPermission =
        statuses[Permission.location]?.isGranted == true ||
        statuses[Permission.locationWhenInUse]?.isGranted == true ||
        statuses[Permission.locationAlways]?.isGranted == true;

    bool hasNotificationPermission = statuses[Permission.notification]?.isGranted == true;

    if (hasLocationPermission) {
      _addLog(_actionLogs, '[Permissions] Location permission granted');
    } else {
      _addLog(_actionLogs, '[Permissions] WARNING: Location permission not granted');
    }

    if (hasNotificationPermission) {
      _addLog(_actionLogs, '[Permissions] Notification permission granted');
    } else {
      _addLog(_actionLogs, '[Permissions] WARNING: Notification permission not granted');
    }

    return;
  }

  Future<void> _initPlatformState() async {
    String platformVersion;
    try {
      platformVersion = await _trackiva.getPlatformVersion() ?? 'Unknown';
    } catch (e) {
      platformVersion = 'Failed to get platform version: $e';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  void _setupEventListeners() {
    // Location events
    _trackiva.onLocation.listen((location) {
      _updateLiveLocation(location);
      _addLog(
        _locationLogs,
        'Location: ${location.latitude.toStringAsFixed(6)}, ${location.longitude.toStringAsFixed(6)} (${location.isBackground ? "Background" : "Foreground"})',
      );
      _addLog(
        _actionLogs,
        '[Location] ${location.latitude.toStringAsFixed(6)}, ${location.longitude.toStringAsFixed(6)} (${location.isBackground ? "BG" : "FG"})',
      );
    });

    // Motion change events
    _trackiva.onMotionChange.listen((location) {
      _addLog(
        _motionLogs,
        'Motion Change: ${location.latitude.toStringAsFixed(6)}, ${location.longitude.toStringAsFixed(6)} (Speed: ${location.speed.toStringAsFixed(2)} m/s)',
      );
      _addLog(_actionLogs, '[MotionChange] Speed: ${location.speed.toStringAsFixed(2)} m/s');
    });

    // Provider change events
    _trackiva.onProviderChange.listen((event) {
      _addLog(_providerLogs, 'Provider Change: GPS=${event.gps}, Network=${event.network}, Enabled=${event.enabled}');
      _addLog(_actionLogs, '[ProviderChange] GPS=${event.gps}, Network=${event.network}, Enabled=${event.enabled}');
    });

    // Activity change events
    _trackiva.onActivityChange.listen((event) {
      _addLog(_activityLogs, 'Activity Change: ${event.activity.toString().split('.').last}');
      _addLog(_actionLogs, '[ActivityChange] ${event.activity.toString().split('.').last}');
    });

    // Geofence events
    _trackiva.onGeofence.listen((event) {
      _addLog(_geofenceLogs, 'Geofence: ${event.identifier} ${event.enter ? "ENTER" : "EXIT"}');
      _addLog(_actionLogs, '[Geofence] ${event.identifier} ${event.enter ? "ENTER" : "EXIT"}');
    });

    // Heartbeat events
    _trackiva.onHeartbeat.listen((event) {
      _addLog(_heartbeatLogs, 'Heartbeat: ${DateTime.now().toIso8601String().substring(11, 19)}');
      _addLog(_actionLogs, '[Heartbeat]');
    });

    // HTTP events
    _trackiva.onHttp.listen((event) {
      _addLog(_httpLogs, 'HTTP: ${event.success ? "SUCCESS" : "FAILED"} (${event.status})');
      _addLog(_actionLogs, '[HTTP] ${event.success ? "SUCCESS" : "FAILED"} (${event.status})');
    });

    // Connectivity change events
    _trackiva.onConnectivityChange.listen((event) {
      _addLog(_connectivityLogs, 'Connectivity: ${event.connected ? "CONNECTED" : "DISCONNECTED"}');
      _addLog(_actionLogs, '[Connectivity] ${event.connected ? "CONNECTED" : "DISCONNECTED"}');
    });

    // Power save change events
    _trackiva.onPowerSaveChange.listen((event) {
      _addLog(_powerSaveLogs, 'Power Save: ${event.isPowerSaveMode ? "ENABLED" : "DISABLED"}');
      _addLog(_actionLogs, '[PowerSave] ${event.isPowerSaveMode ? "ENABLED" : "DISABLED"}');
    });
  }

  void _updateLiveLocation(LocationData location) {
    setState(() {
      _currentLocation = location;
      _locationCount++;
      _lastLocationTime = DateTime.now();

      // Add to history (keep last 100 locations)
      _locationHistory.add(location);
      if (_locationHistory.length > 100) {
        _locationHistory.removeAt(0);
      }

      // Calculate total distance
      if (_locationHistory.length > 1) {
        final previousLocation = _locationHistory[_locationHistory.length - 2];
        final distance = _calculateDistance(
          previousLocation.latitude,
          previousLocation.longitude,
          location.latitude,
          location.longitude,
        );
        _totalDistance += distance;
      }

      // Calculate average speed
      if (_locationHistory.length > 1) {
        double totalSpeed = 0;
        int speedCount = 0;
        for (final loc in _locationHistory) {
          if (loc.speed > 0) {
            totalSpeed += loc.speed;
            speedCount++;
          }
        }
        _averageSpeed = speedCount > 0 ? totalSpeed / speedCount : 0;
      }
    });
  }

  double _calculateDistance(double lat1, double lon1, double lat2, double lon2) {
    const double earthRadius = 6371000; // Earth's radius in meters
    final double dLat = _degreesToRadians(lat2 - lat1);
    final double dLon = _degreesToRadians(lon2 - lon1);
    final double a =
        sin(dLat / 2) * sin(dLat / 2) +
        sin(_degreesToRadians(lat1)) * sin(_degreesToRadians(lat2)) * sin(dLon / 2) * sin(dLon / 2);
    final double c = 2 * asin(sqrt(a));
    return earthRadius * c;
  }

  double _degreesToRadians(double degrees) {
    return degrees * (3.14159265359 / 180);
  }

  void _addLog(List<String> logList, String message) {
    setState(() {
      final time = DateTime.now().toIso8601String().substring(11, 19);
      logList.add('$time: $message');
      if (logList.length > 50) {
        logList.removeAt(0);
      }
    });
  }

  Future<void> _configurePlugin() async {
    try {
      setState(() {
        _status = 'Configuring...';
      });
      _addLog(_actionLogs, '[Action] Configuring plugin...');
      // Configure with current settings
      final state = await _trackiva.ready(_config);
      setState(() {
        _status = 'Configured successfully';
        _isConfigured = true;
        _isTracking = state.enabled;
        _currentState = state;
      });
      _addLog(_actionLogs, '[Action] Plugin configured. Tracking: ${state.enabled ? "ON" : "OFF"}');
    } catch (e) {
      setState(() {
        _status = 'Configuration failed: $e';
      });
      _addLog(_actionLogs, '[Error] Configuration failed: $e');
    }
  }

  Future<void> _startTracking() async {
    try {
      // Set all MQTT and user/device/job details before starting tracking
      _trackiva.setMqttConfigAndDetails(
        broker: "broker.hivemq.com",
        port: 8884,
        username: "user",
        password: "pass",
        topic: "trackiva/location",
        payload: {
          "userId": "17826",
          "batteryLevel": 28,
          "userType": "Mechanic",
          "deviceId": "da8905647e2853b8",
          "domain": "dha",
          "usernameField": "thahir",
          "identifier": "thahir@dha",
          "skills": [],
          "status": "ONLINE",
          "name": "THAHIR AbduKareem",
          "geofence": "Muhaisnah MFC Area",
          "emailid": "riyas@gmail.com",
          "mobile": "000000000",
          "jobId": "",
        },
      );
      setState(() {
        _status = 'Starting tracking...';
      });
      _addLog(_actionLogs, '[Action] Starting tracking...');
      final success = await _trackiva.start();
      if (success) {
        setState(() {
          _status = 'Tracking active';
          _isTracking = true;
        });
        _addLog(_actionLogs, '[Action] Tracking started.');
      } else {
        setState(() {
          _status = 'Start tracking failed';
        });
        _addLog(_actionLogs, '[Error] Start tracking failed.');
      }
    } catch (e) {
      setState(() {
        _status = 'Start tracking failed: $e';
      });
      _addLog(_actionLogs, '[Error] Start tracking failed: $e');
      // If it's a permission error, show a helpful message
      if (e.toString().contains('PERMISSION_DENIED')) {
        _addLog(_actionLogs, '[Error] Please grant location permissions in app settings');
      }
    }
  }

  Future<void> _stopTracking() async {
    try {
      setState(() {
        _status = 'Stopping tracking...';
      });
      _addLog(_actionLogs, '[Action] Stopping tracking...');
      final success = await _trackiva.stop();
      if (success) {
        setState(() {
          _status = 'Tracking stopped';
          _isTracking = false;
        });
        _addLog(_actionLogs, '[Action] Tracking stopped.');
      } else {
        setState(() {
          _status = 'Stop tracking failed';
        });
        _addLog(_actionLogs, '[Error] Stop tracking failed.');
      }
    } catch (e) {
      setState(() {
        _status = 'Stop tracking failed: $e';
      });
      _addLog(_actionLogs, '[Error] Stop tracking failed: $e');
    }
  }

  Future<void> _getCurrentPosition() async {
    try {
      setState(() {
        _status = 'Getting current position...';
      });
      _addLog(_actionLogs, '[Action] Getting current position...');
      final location = await _trackiva.getCurrentPosition();
      if (location != null) {
        _updateLiveLocation(location);
        _addLog(
          _locationLogs,
          'CURRENT: ${location.latitude.toStringAsFixed(6)}, ${location.longitude.toStringAsFixed(6)}',
        );
        setState(() {
          _status = 'Current position retrieved';
        });
        _addLog(_actionLogs, '[Action] Got current position.');
      } else {
        setState(() {
          _status = 'Failed to get current position';
        });
        _addLog(_actionLogs, '[Error] Failed to get current position.');
      }
    } catch (e) {
      setState(() {
        _status = 'Get current position failed: $e';
      });
      _addLog(_actionLogs, '[Error] Get current position failed: $e');
    }
  }

  Future<void> _getState() async {
    try {
      final state = await _trackiva.getState();
      setState(() {
        _currentState = state;
        _status =
            'State: enabled=${state.enabled}, moving=${state.isMoving}, locationServices=${state.locationServicesEnabled}';
      });
      _addLog(_actionLogs, '[Action] Got state. Tracking: ${state.enabled ? "ON" : "OFF"}');
    } catch (e) {
      setState(() {
        _status = 'Get state failed: $e';
      });
      _addLog(_actionLogs, '[Error] Get state failed: $e');
    }
  }

  Future<void> _checkLocationServices() async {
    try {
      final isEnabled = await _trackiva.isLocationServicesEnabled();
      setState(() {
        _status = 'Location services: ${isEnabled ? "Enabled" : "Disabled"}';
      });
      _addLog(_actionLogs, '[Action] Location services: ${isEnabled ? "Enabled" : "Disabled"}');
    } catch (e) {
      setState(() {
        _status = 'Check location services failed: $e';
      });
      _addLog(_actionLogs, '[Error] Check location services failed: $e');
    }
  }

  Future<void> _getTrackingStats() async {
    try {
      final stats = await _trackiva.getTrackingStats();
      setState(() {
        _status =
            'Stats: ${stats.totalLocations} locations, ${stats.totalDistance.toStringAsFixed(2)}m distance, ${stats.averageSpeed.toStringAsFixed(2)} m/s avg speed';
      });
      _addLog(_actionLogs, '[Action] Got tracking stats.');
    } catch (e) {
      setState(() {
        _status = 'Get tracking stats failed: $e';
      });
      _addLog(_actionLogs, '[Error] Get tracking stats failed: $e');
    }
  }

  Future<void> _clearTrackingData() async {
    try {
      final success = await _trackiva.clearTrackingData();
      if (success) {
        setState(() {
          _locationLogs.clear();
          _motionLogs.clear();
          _providerLogs.clear();
          _activityLogs.clear();
          _geofenceLogs.clear();
          _heartbeatLogs.clear();
          _httpLogs.clear();
          _connectivityLogs.clear();
          _powerSaveLogs.clear();
          _locationHistory.clear();
          _currentLocation = null;
          _totalDistance = 0.0;
          _averageSpeed = 0.0;
          _locationCount = 0;
          _lastLocationTime = null;
          _status = 'Tracking data cleared';
        });
        _addLog(_actionLogs, '[Action] Tracking data cleared.');
      } else {
        setState(() {
          _status = 'Failed to clear tracking data';
        });
        _addLog(_actionLogs, '[Error] Failed to clear tracking data.');
      }
    } catch (e) {
      setState(() {
        _status = 'Clear tracking data failed: $e';
      });
      _addLog(_actionLogs, '[Error] Clear tracking data failed: $e');
    }
  }

  Future<void> _checkMqttStatus() async {
    try {
      final status = await _trackiva.getMqttStatus();
      setState(() {
        _status = 'MQTT Status: ${status['connected'] ? "Connected" : "Disconnected"} (${status['connectionState']})';
      });
      _addLog(
        _actionLogs,
        '[Action] MQTT Status: ${status['connected'] ? "Connected" : "Disconnected"} (${status['connectionState']})',
      );
      _addLog(_actionLogs, '[Action] MQTT Broker: ${status['broker']}:${status['port']}');
      _addLog(_actionLogs, '[Action] MQTT Topic: ${status['topic']}');
      _addLog(_actionLogs, '[Action] MQTT UserId: ${status['userId']}');
    } catch (e) {
      setState(() {
        _status = 'Check MQTT status failed: $e';
      });
      _addLog(_actionLogs, '[Error] Check MQTT status failed: $e');
    }
  }

  void _updateConfig() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Configure Trackiva'),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              DropdownButtonFormField<DesiredAccuracy>(
                initialValue: _config.desiredAccuracy,
                decoration: InputDecoration(labelText: 'Desired Accuracy'),
                items: DesiredAccuracy.values.map((accuracy) {
                  return DropdownMenuItem(
                    value: accuracy,
                    child: Text(accuracy.toString().split('.').last.toUpperCase()),
                  );
                }).toList(),
                onChanged: (value) {
                  if (value != null) {
                    setState(() {
                      _config = TrackivaConfig(
                        desiredAccuracy: value,
                        distanceFilter: _config.distanceFilter,
                        stopOnTerminate: _config.stopOnTerminate,
                        startOnBoot: _config.startOnBoot,
                        debug: _config.debug,
                        logLevel: _config.logLevel,
                        enableBatteryOptimization: _config.enableBatteryOptimization,
                        notificationTitle: _config.notificationTitle,
                        notificationText: _config.notificationText,
                        interval: _config.interval,
                        fastestInterval: _config.fastestInterval,
                        showLocationNotifications: _config.showLocationNotifications,
                      );
                    });
                  }
                },
              ),
              SizedBox(height: 16),
              TextFormField(
                initialValue: _config.distanceFilter.toString(),
                decoration: InputDecoration(labelText: 'Distance Filter (meters)'),
                keyboardType: TextInputType.number,
                onChanged: (value) {
                  final distance = double.tryParse(value) ?? 10.0;
                  setState(() {
                    _config = TrackivaConfig(
                      desiredAccuracy: _config.desiredAccuracy,
                      distanceFilter: distance,
                      stopOnTerminate: _config.stopOnTerminate,
                      startOnBoot: _config.startOnBoot,
                      debug: _config.debug,
                      logLevel: _config.logLevel,
                      enableBatteryOptimization: _config.enableBatteryOptimization,
                      notificationTitle: _config.notificationTitle,
                      notificationText: _config.notificationText,
                      interval: _config.interval,
                      fastestInterval: _config.fastestInterval,
                      showLocationNotifications: _config.showLocationNotifications,
                    );
                  });
                },
              ),
              SizedBox(height: 16),
              CheckboxListTile(
                title: Text('Stop on Terminate'),
                value: _config.stopOnTerminate,
                onChanged: (value) {
                  setState(() {
                    _config = TrackivaConfig(
                      desiredAccuracy: _config.desiredAccuracy,
                      distanceFilter: _config.distanceFilter,
                      stopOnTerminate: value ?? false,
                      startOnBoot: _config.startOnBoot,
                      debug: _config.debug,
                      logLevel: _config.logLevel,
                      enableBatteryOptimization: _config.enableBatteryOptimization,
                      notificationTitle: _config.notificationTitle,
                      notificationText: _config.notificationText,
                      interval: _config.interval,
                      fastestInterval: _config.fastestInterval,
                      showLocationNotifications: _config.showLocationNotifications,
                    );
                  });
                },
              ),
              CheckboxListTile(
                title: Text('Start on Boot'),
                value: _config.startOnBoot,
                onChanged: (value) {
                  setState(() {
                    _config = TrackivaConfig(
                      desiredAccuracy: _config.desiredAccuracy,
                      distanceFilter: _config.distanceFilter,
                      stopOnTerminate: _config.stopOnTerminate,
                      startOnBoot: value ?? true,
                      debug: _config.debug,
                      logLevel: _config.logLevel,
                      enableBatteryOptimization: _config.enableBatteryOptimization,
                      notificationTitle: _config.notificationTitle,
                      notificationText: _config.notificationText,
                      interval: _config.interval,
                      fastestInterval: _config.fastestInterval,
                      showLocationNotifications: _config.showLocationNotifications,
                    );
                  });
                },
              ),
              CheckboxListTile(
                title: Text('Debug Mode'),
                value: _config.debug,
                onChanged: (value) {
                  setState(() {
                    _config = TrackivaConfig(
                      desiredAccuracy: _config.desiredAccuracy,
                      distanceFilter: _config.distanceFilter,
                      stopOnTerminate: _config.stopOnTerminate,
                      startOnBoot: _config.startOnBoot,
                      debug: value ?? true,
                      logLevel: _config.logLevel,
                      enableBatteryOptimization: _config.enableBatteryOptimization,
                      notificationTitle: _config.notificationTitle,
                      notificationText: _config.notificationText,
                      interval: _config.interval,
                      fastestInterval: _config.fastestInterval,
                      showLocationNotifications: _config.showLocationNotifications,
                    );
                  });
                },
              ),
            ],
          ),
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              _configurePlugin();
            },
            child: Text('Apply'),
          ),
        ],
      ),
    );
  }

  Widget _buildLiveLocationDisplay() {
    if (_currentLocation == null) {
      return Card(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'Live Location Tracking',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.blue),
              ),
              SizedBox(height: 8),
              Text('No location data available', style: TextStyle(color: Colors.grey)),
            ],
          ),
        ),
      );
    }

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.location_on, color: Colors.red, size: 24),
                SizedBox(width: 8),
                Text(
                  'Live Location Tracking',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.blue),
                ),
                Spacer(),
                Container(
                  padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: _currentLocation!.isBackground ? Colors.orange : Colors.green,
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    _currentLocation!.isBackground ? 'BACKGROUND' : 'FOREGROUND',
                    style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
            SizedBox(height: 16),

            // Coordinates
            Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Latitude', style: TextStyle(fontSize: 12, color: Colors.grey)),
                      Text(
                        _currentLocation!.latitude.toStringAsFixed(8),
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
                      ),
                    ],
                  ),
                ),
                SizedBox(width: 16),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Longitude', style: TextStyle(fontSize: 12, color: Colors.grey)),
                      Text(
                        _currentLocation!.longitude.toStringAsFixed(8),
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
                      ),
                    ],
                  ),
                ),
              ],
            ),
            SizedBox(height: 16),

            // Speed and Accuracy
            Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Speed', style: TextStyle(fontSize: 12, color: Colors.grey)),
                      Text(
                        '${_currentLocation!.speed.toStringAsFixed(2)} m/s',
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.green),
                      ),
                    ],
                  ),
                ),
                SizedBox(width: 16),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Accuracy', style: TextStyle(fontSize: 12, color: Colors.grey)),
                      Text(
                        '${_currentLocation!.accuracy.toStringAsFixed(1)}m',
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blue),
                      ),
                    ],
                  ),
                ),
                SizedBox(width: 16),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Altitude', style: TextStyle(fontSize: 12, color: Colors.grey)),
                      Text(
                        '${_currentLocation!.altitude.toStringAsFixed(1)}m',
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.purple),
                      ),
                    ],
                  ),
                ),
              ],
            ),
            SizedBox(height: 16),

            // Tracking Stats
            Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8)),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Tracking Statistics', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)),
                  SizedBox(height: 8),
                  Row(
                    children: [
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text('Total Distance', style: TextStyle(fontSize: 11, color: Colors.grey)),
                            Text(
                              '${_totalDistance.toStringAsFixed(2)}m',
                              style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
                            ),
                          ],
                        ),
                      ),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text('Avg Speed', style: TextStyle(fontSize: 11, color: Colors.grey)),
                            Text(
                              '${_averageSpeed.toStringAsFixed(2)} m/s',
                              style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
                            ),
                          ],
                        ),
                      ),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text('Locations', style: TextStyle(fontSize: 11, color: Colors.grey)),
                            Text('$_locationCount', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)),
                          ],
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
            SizedBox(height: 8),

            // Last Update Time
            if (_lastLocationTime != null) ...[
              Text(
                'Last Update: ${_lastLocationTime!.toIso8601String().substring(11, 19)}',
                style: TextStyle(fontSize: 12, color: Colors.grey, fontStyle: FontStyle.italic),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildLogSection(String title, List<String> logs, Color color) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: color),
            ),
            SizedBox(height: 8),
            Container(
              height: 150,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.grey.shade300),
                borderRadius: BorderRadius.circular(8),
              ),
              child: ListView.builder(
                itemCount: logs.length,
                itemBuilder: (context, index) {
                  final log = logs[logs.length - 1 - index];
                  return Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
                    child: Text(
                      log,
                      style: TextStyle(color: color, fontSize: 11, fontFamily: 'monospace'),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildLoggerPanel() {
    return Card(
      color: Colors.black,
      margin: const EdgeInsets.only(top: 8),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SizedBox(
          height: 120,
          child: ListView.builder(
            reverse: true,
            itemCount: _actionLogs.length,
            itemBuilder: (context, index) {
              final log = _actionLogs[_actionLogs.length - 1 - index];
              return Text(
                log,
                style: const TextStyle(color: Colors.greenAccent, fontFamily: 'monospace', fontSize: 12),
              );
            },
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Trackiva Demo'),
          backgroundColor: Colors.green,
          actions: [IconButton(icon: Icon(Icons.settings), onPressed: _updateConfig)],
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // Tracking Status Bar
              Container(
                width: double.infinity,
                padding: const EdgeInsets.symmetric(vertical: 12),
                decoration: BoxDecoration(
                  color: _isTracking ? Colors.green : Colors.red,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Center(
                  child: Text(
                    _isTracking ? 'TRACKING: ON' : 'TRACKING: OFF',
                    style: const TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                      letterSpacing: 2,
                    ),
                  ),
                ),
              ),
              const SizedBox(height: 12),

              // Live Location Display
              _buildLiveLocationDisplay(),
              SizedBox(height: 16),

              // Status Section
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Status', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
                      SizedBox(height: 8),
                      Text('Platform: $_platformVersion'),
                      Text('Status: $_status'),
                      Text('Configured: ${_isConfigured ? "Yes" : "No"}'),
                      Text('Tracking: ${_isTracking ? "Active" : "Inactive"}'),
                      if (_currentState != null) ...[
                        Text('Enabled: ${_currentState!.enabled}'),
                        Text('Moving: ${_currentState!.isMoving}'),
                        Text('Location Services: ${_currentState!.locationServicesEnabled}'),
                        Text('Authorization: ${_currentState!.authorization.toString().split('.').last}'),
                        Text('Activity: ${_currentState!.activity.toString().split('.').last}'),
                      ],
                    ],
                  ),
                ),
              ),
              SizedBox(height: 16),

              // Control Buttons
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Controls', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
                      SizedBox(height: 16),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? null : _configurePlugin,
                              child: Text('Configure'),
                            ),
                          ),
                          SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured && !_isTracking ? _startTracking : null,
                              child: Text('Start'),
                            ),
                          ),
                          SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(onPressed: _isTracking ? _stopTracking : null, child: Text('Stop')),
                          ),
                        ],
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _getCurrentPosition : null,
                              child: Text('Get Position'),
                            ),
                          ),
                          SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _getState : null,
                              child: Text('Get State'),
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _checkLocationServices : null,
                              child: Text('Check Services'),
                            ),
                          ),
                          SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _getTrackingStats : null,
                              child: Text('Get Stats'),
                            ),
                          ),
                          SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _clearTrackingData : null,
                              child: Text('Clear Data'),
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton(
                              onPressed: _isConfigured ? _checkMqttStatus : null,
                              child: Text('Check MQTT'),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              SizedBox(height: 16),

              // Event Logs
              Text('Event Logs', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
              SizedBox(height: 8),

              _buildLogSection('Location Events', _locationLogs, Colors.blue),
              SizedBox(height: 8),

              _buildLogSection('Motion Change Events', _motionLogs, Colors.green),
              SizedBox(height: 8),

              _buildLogSection('Provider Change Events', _providerLogs, Colors.orange),
              SizedBox(height: 8),

              _buildLogSection('Activity Change Events', _activityLogs, Colors.purple),
              SizedBox(height: 8),

              _buildLogSection('Geofence Events', _geofenceLogs, Colors.red),
              SizedBox(height: 8),

              _buildLogSection('Heartbeat Events', _heartbeatLogs, Colors.teal),
              SizedBox(height: 8),

              _buildLogSection('HTTP Events', _httpLogs, Colors.indigo),
              SizedBox(height: 8),

              _buildLogSection('Connectivity Events', _connectivityLogs, Colors.cyan),
              SizedBox(height: 8),

              _buildLogSection('Power Save Events', _powerSaveLogs, Colors.amber),
              SizedBox(height: 16),

              // Logger Panel
              Text('Logger', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
              _buildLoggerPanel(),
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
160
points
54
downloads

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter plugin for location tracking in foreground, background, and terminated states with MQTT, GraphQL, and HTTP support.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

connectivity_plus, flutter, gql, graphql, http, mqtt_client, path, path_provider, plugin_platform_interface, sqflite

More

Packages that depend on trackiva

Packages that implement trackiva