πŸš— Flutter Traccar API

pub package License: MIT Flutter Dart

A powerful, feature-rich Flutter package for seamless integration with Traccar GPS tracking servers

πŸ“– Documentation β€’ πŸš€ Quick Start β€’ πŸ’‘ Examples β€’ 🀝 Contributing


✨ Features

πŸ” Authentication & Security

  • βœ… Secure login/logout with credential caching
  • βœ… Encrypted storage using flutter_secure_storage
  • βœ… Automatic session management
  • βœ… Token-based authentication

πŸ“± Device Management

  • βœ… Retrieve and manage GPS tracking devices
  • βœ… Real-time device status monitoring
  • βœ… Device configuration and settings
  • βœ… Bulk device operations

πŸ“ Position Tracking

  • βœ… Real-time position updates
  • βœ… Historical position data
  • βœ… Geofence monitoring
  • βœ… Route optimization

πŸ“Š Advanced Reporting

  • βœ… Trip analysis and reports
  • βœ… Stop detection and analysis
  • βœ… Summary statistics
  • βœ… Distance calculations

πŸ”„ Real-time WebSocket

  • βœ… Live device status updates
  • βœ… Real-time position streaming
  • βœ… Event notifications (alarms, geofences)
  • βœ… Automatic reconnection & heartbeat
  • βœ… Connection status monitoring

πŸš€ Performance & Optimization

Feature Description Benefits
🧠 Intelligent Caching Smart cache management with TTL Faster responses, offline support
⚑ Rate Limiting Built-in API rate limiting Prevents server overload
πŸ”„ Request Batching Automatic request optimization Improved performance
πŸ“¦ Offline Mode Cache-based offline functionality Works without internet
πŸ—οΈ Type Safety Full OpenAPI-generated models Compile-time error checking

πŸ“¦ Installation

Add this package to your pubspec.yaml:

dependencies:
  flutter_traccar_api: ^0.1.0
  # Required for secure storage
  flutter_secure_storage: ^9.0.0
  # Required for caching
  shared_preferences: ^2.2.0

Then run:

flutter pub get

πŸ”§ Platform Setup

πŸ“± Android Setup

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
🍎 iOS Setup

No additional setup required for iOS.

🌐 Web Setup

Ensure your Traccar server supports CORS for web applications.


πŸš€ Quick Start

1️⃣ Initialize the API

import 'package:flutter_traccar_api/flutter_traccar_api.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 🎯 Basic initialization
  await FlutterTraccarApi.initialize('https://your-traccar-server.com');
  
  // πŸš€ Advanced initialization with performance features
  await FlutterTraccarApi.initialize(
    'https://your-traccar-server.com',
    config: HttpClientConfig(
      enableCaching: true,
      enableRateLimiting: true,
      enableBatching: true,
      enableOfflineMode: true,
      cacheConfig: CacheConfig(
        maxCacheSize: 50 * 1024 * 1024, // 50MB
        defaultTtl: Duration(minutes: 15),
        enableCompression: true,
      ),
      rateLimitConfig: RateLimitConfig(
        requestsPerSecond: 10,
        burstSize: 20,
        enableBackoff: true,
      ),
    ),
  );
  
  runApp(MyApp());
}

2️⃣ Authentication

class AuthService {
  final api = FlutterTraccarApi.instance;

  Future<bool> login(String username, String password) async {
    try {
      final user = await api.login(username, password);
      print('βœ… Logged in as: ${user.name}');
      return true;
    } on TraccarApiException catch (e) {
      print('❌ Login failed: ${e.message}');
      return false;
    }
  }

  Future<void> logout() async {
    await api.logout();
    print('πŸ‘‹ Logged out successfully');
  }

  Future<bool> isLoggedIn() async {
    return await api.isAuthenticated();
  }
}

3️⃣ Device Management

class DeviceService {
  final api = FlutterTraccarApi.instance;

  Future<List<Device>> getAllDevices() async {
    try {
      final devices = await api.getDevices();
      print('πŸ“± Found ${devices.length} devices');
      return devices;
    } catch (e) {
      print('❌ Error fetching devices: $e');
      return [];
    }
  }

  Future<Device?> getDeviceById(int deviceId) async {
    try {
      return await api.getDevice(deviceId);
    } catch (e) {
      print('❌ Device not found: $e');
      return null;
    }
  }
}

4️⃣ Position Tracking

class PositionService {
  final api = FlutterTraccarApi.instance;

  Future<List<Position>> getRecentPositions(int deviceId) async {
    final now = DateTime.now();
    final yesterday = now.subtract(Duration(days: 1));
    
    return await api.getPositions(
      deviceId: deviceId,
      from: yesterday,
      to: now,
    );
  }

  Future<Position?> getLatestPosition(int deviceId) async {
    final positions = await getRecentPositions(deviceId);
    return positions.isNotEmpty ? positions.first : null;
  }
}

5️⃣ Real-time WebSocket Updates

class WebSocketService {
  final api = FlutterTraccarApi.instance;

  Future<void> startRealTimeUpdates() async {
    // Connect to WebSocket
    final connected = await api.connectWebSocket();
    if (!connected) {
      print('❌ Failed to connect to WebSocket');
      return;
    }

    // Listen to real-time device updates
    api.deviceUpdatesStream.listen((devices) {
      print('πŸ“± Device updates: ${devices.length} devices');
    });

    // Listen to real-time position updates
    api.positionUpdatesStream.listen((positions) {
      print('πŸ“ Position updates: ${positions.length} positions');
    });

    // Listen to real-time events (alarms, geofences, etc.)
    api.eventUpdatesStream.listen((events) {
      print('🚨 Events: ${events.length} events');
    });

    // Monitor connection status
    api.webSocketStatusStream.listen((status) {
      print('πŸ”— WebSocket status: ${status.name}');
    });
  }

  Future<void> stopRealTimeUpdates() async {
    await api.disconnectWebSocket();
    print('πŸ”Œ WebSocket disconnected');
  }
}

πŸ’‘ Examples

🎯 Complete Flutter App Example

import 'package:flutter/material.dart';
import 'package:flutter_traccar_api/flutter_traccar_api.dart';

class TraccarDashboard extends StatefulWidget {
  @override
  _TraccarDashboardState createState() => _TraccarDashboardState();
}

class _TraccarDashboardState extends State<TraccarDashboard> {
  final api = FlutterTraccarApi.instance;
  List<Device> devices = [];
  bool isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadDevices();
  }

  Future<void> _loadDevices() async {
    try {
      final fetchedDevices = await api.getDevices();
      setState(() {
        devices = fetchedDevices;
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
      _showError('Failed to load devices: $e');
    }
  }

  void _showError(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message), backgroundColor: Colors.red),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('πŸš— Traccar Dashboard'),
        actions: [
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: _loadDevices,
          ),
        ],
      ),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : devices.isEmpty
              ? Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(Icons.devices, size: 64, color: Colors.grey),
                      SizedBox(height: 16),
                      Text('No devices found'),
                    ],
                  ),
                )
              : ListView.builder(
                  itemCount: devices.length,
                  itemBuilder: (context, index) {
                    final device = devices[index];
                    return DeviceCard(device: device);
                  },
                ),
    );
  }
}

class DeviceCard extends StatelessWidget {
  final Device device;

  const DeviceCard({Key? key, required this.device}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(8),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: device.status == 'online' ? Colors.green : Colors.red,
          child: Icon(Icons.gps_fixed, color: Colors.white),
        ),
        title: Text(device.name ?? 'Unknown Device'),
        subtitle: Text('ID: ${device.id} β€’ Status: ${device.status}'),
        trailing: Icon(Icons.arrow_forward_ios),
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => DeviceDetailPage(device: device),
            ),
          );
        },
      ),
    );
  }
}

πŸ“Š Advanced Reporting Example

class ReportService {
  final api = FlutterTraccarApi.instance;

  Future<Map<String, dynamic>> generateComprehensiveReport(
    List<int> deviceIds,
    DateTime from,
    DateTime to,
  ) async {
    try {
      // πŸš— Get trip reports
      final trips = await api.getTripReports(
        deviceIds: deviceIds,
        from: from,
        to: to,
      );

      // πŸ“Š Get summary reports
      final summaries = await api.getSummaryReports(
        deviceIds: deviceIds,
        from: from,
        to: to,
      );

      // πŸ›‘ Get stop reports
      final stops = await api.getStopReports(
        deviceIds: deviceIds,
        from: from,
        to: to,
      );

      return {
        'trips': trips,
        'summaries': summaries,
        'stops': stops,
        'totalDistance': summaries.fold<double>(
          0,
          (sum, summary) => sum + (summary.distance ?? 0),
        ),
        'totalDuration': trips.fold<Duration>(
          Duration.zero,
          (sum, trip) => sum + (trip.duration ?? Duration.zero),
        ),
      };
    } catch (e) {
      throw Exception('Failed to generate report: $e');
    }
  }
}

πŸ”„ Real-time WebSocket Updates

The package provides powerful WebSocket functionality for real-time updates without polling:

πŸš€ Basic WebSocket Usage

class RealTimeTracker {
  final api = FlutterTraccarApi.instance;
  late StreamSubscription<List<Device>> _deviceSubscription;
  late StreamSubscription<List<Position>> _positionSubscription;
  late StreamSubscription<List<Event>> _eventSubscription;
  late StreamSubscription<WebSocketStatus> _statusSubscription;

  Future<void> startRealTimeTracking() async {
    // Connect to WebSocket
    final connected = await api.connectWebSocket();
    if (!connected) {
      throw Exception('Failed to connect to WebSocket');
    }

    // Listen to real-time device updates
    _deviceSubscription = api.deviceUpdatesStream.listen(
      (devices) {
        print('πŸ“± Received ${devices.length} device updates');
        // Handle device updates (status changes, etc.)
      },
      onError: (error) => print('❌ Device stream error: $error'),
    );

    // Listen to real-time position updates
    _positionSubscription = api.positionUpdatesStream.listen(
      (positions) {
        print('πŸ“ Received ${positions.length} position updates');
        // Handle position updates (location changes)
      },
      onError: (error) => print('❌ Position stream error: $error'),
    );

    // Listen to real-time events
    _eventSubscription = api.eventUpdatesStream.listen(
      (events) {
        print('🚨 Received ${events.length} events');
        // Handle events (alarms, geofence violations, etc.)
      },
      onError: (error) => print('❌ Event stream error: $error'),
    );

    // Monitor WebSocket connection status
    _statusSubscription = api.webSocketStatusStream.listen(
      (status) {
        switch (status) {
          case WebSocketStatus.connected:
            print('βœ… WebSocket connected');
            break;
          case WebSocketStatus.disconnected:
            print('❌ WebSocket disconnected');
            break;
          case WebSocketStatus.connecting:
            print('πŸ”„ WebSocket connecting...');
            break;
          case WebSocketStatus.reconnecting:
            print('πŸ”„ WebSocket reconnecting...');
            break;
          case WebSocketStatus.error:
            print('❌ WebSocket error');
            break;
        }
      },
    );
  }

  Future<void> stopRealTimeTracking() async {
    // Cancel all subscriptions
    await _deviceSubscription.cancel();
    await _positionSubscription.cancel();
    await _eventSubscription.cancel();
    await _statusSubscription.cancel();
    
    // Disconnect WebSocket
    await api.disconnectWebSocket();
  }

  bool get isConnected => api.isWebSocketConnected;
}

🎯 Advanced WebSocket Configuration

// Initialize with custom WebSocket configuration
await FlutterTraccarApi.initialize(
  'https://your-traccar-server.com',
  config: HttpClientConfig(
    webSocketConfig: WebSocketConfig(
      enableAutoReconnect: true,
      maxReconnectAttempts: 5,
      reconnectInterval: Duration(seconds: 5),
      heartbeatInterval: Duration(seconds: 30),
    ),
  ),
);

πŸ“± Flutter Widget Integration

class LiveTrackingWidget extends StatefulWidget {
  @override
  _LiveTrackingWidgetState createState() => _LiveTrackingWidgetState();
}

class _LiveTrackingWidgetState extends State<LiveTrackingWidget> {
  final api = FlutterTraccarApi.instance;
  List<Device> devices = [];
  List<Position> positions = [];
  WebSocketStatus connectionStatus = WebSocketStatus.disconnected;

  @override
  void initState() {
    super.initState();
    _initializeWebSocket();
  }

  Future<void> _initializeWebSocket() async {
    // Connect to WebSocket
    await api.connectWebSocket();

    // Listen to streams
    api.deviceUpdatesStream.listen((updatedDevices) {
      setState(() => devices = updatedDevices);
    });

    api.positionUpdatesStream.listen((updatedPositions) {
      setState(() => positions = updatedPositions);
    });

    api.webSocketStatusStream.listen((status) {
      setState(() => connectionStatus = status);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Live Tracking'),
        actions: [
          Icon(
            connectionStatus == WebSocketStatus.connected
                ? Icons.wifi
                : Icons.wifi_off,
            color: connectionStatus == WebSocketStatus.connected
                ? Colors.green
                : Colors.red,
          ),
        ],
      ),
      body: Column(
        children: [
          // Connection status
          Container(
            padding: EdgeInsets.all(8),
            color: connectionStatus == WebSocketStatus.connected
                ? Colors.green.withOpacity(0.1)
                : Colors.red.withOpacity(0.1),
            child: Row(
              children: [
                Icon(
                  connectionStatus == WebSocketStatus.connected
                      ? Icons.check_circle
                      : Icons.error,
                  color: connectionStatus == WebSocketStatus.connected
                      ? Colors.green
                      : Colors.red,
                ),
                SizedBox(width: 8),
                Text('Status: ${connectionStatus.name}'),
              ],
            ),
          ),
          // Live device list
          Expanded(
            child: ListView.builder(
              itemCount: devices.length,
              itemBuilder: (context, index) {
                final device = devices[index];
                final position = positions
                    .where((p) => p.deviceId == device.id)
                    .lastOrNull;
                
                return ListTile(
                  leading: CircleAvatar(
                    backgroundColor: device.status == 'online'
                        ? Colors.green
                        : Colors.grey,
                    child: Icon(Icons.device_hub, color: Colors.white),
                  ),
                  title: Text(device.name ?? 'Unknown Device'),
                  subtitle: position != null
                      ? Text(
                          'Lat: ${position.latitude?.toStringAsFixed(6)}, '
                          'Lng: ${position.longitude?.toStringAsFixed(6)}\n'
                          'Speed: ${position.speed?.toStringAsFixed(1)} km/h',
                        )
                      : Text('No position data'),
                  trailing: Text(
                    position?.deviceTime ?? 'No update',
                    style: TextStyle(fontSize: 12),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    api.disconnectWebSocket();
    super.dispose();
  }
}

πŸŽ›οΈ Advanced Configuration

🧠 Cache Management

// Get cache statistics
final stats = await api.getCacheStats();
print('πŸ“Š Cache Stats:');
print('  Size: ${(stats.totalSize / 1024 / 1024).toStringAsFixed(2)} MB');
print('  Hit Rate: ${stats.hitRate.toStringAsFixed(1)}%');
print('  Entries: ${stats.entryCount}');

// Clear specific caches
await api.invalidateDeviceCache();
await api.invalidatePositionCache();

// Clear all cache
await api.clearCache();

⚑ Rate Limiting

// Check rate limit status
final status = api.getRateLimitStatus();
if (status != null) {
  print('⚑ Rate Limit Status:');
  print('  Remaining: ${status.remainingRequests}');
  print('  Reset: ${status.resetTime}');
  print('  Limit: ${status.requestLimit}');
}

// Reset rate limiter
api.resetRateLimit();

πŸ”„ Request Batching

// Get batching statistics
final batchStats = api.getBatchingStats();
if (batchStats != null) {
  print('πŸ”„ Batch Stats:');
  print('  Total Batches: ${batchStats.totalBatches}');
  print('  Avg Size: ${batchStats.averageBatchSize.toStringAsFixed(1)}');
  print('  Pending: ${batchStats.pendingRequests}');
}

// Flush pending batches
await api.flushAllBatches();

πŸ› οΈ API Reference

πŸ” Authentication Methods
// Login with credentials
Future<User> login(String email, String password);

// Logout and clear session
Future<void> logout();

// Check authentication status
Future<bool> isAuthenticated();

// Get current username
Future<String?> currentUsername();

// Check for cached credentials
Future<bool> hasCachedCredentials();
πŸ“± Device Management
// Get all devices
Future<List<Device>> getDevices();

// Get specific device
Future<Device> getDevice(int deviceId);

// Get device with caching
Future<List<Device>> getDevicesCached();
πŸ“ Position Tracking
// Get positions with filters
Future<List<Position>> getPositions({
  int? deviceId,
  List<int>? deviceIds,
  DateTime? from,
  DateTime? to,
});

// Get latest positions
Future<List<Position>> getLatestPositions(List<int> deviceIds);
πŸ“Š Reporting
// Trip reports
Future<List<TripReport>> getTripReports({
  required List<int> deviceIds,
  required DateTime from,
  required DateTime to,
});

// Summary reports
Future<List<SummaryReport>> getSummaryReports({
  required List<int> deviceIds,
  required DateTime from,
  required DateTime to,
});

// Stop reports
Future<List<StopReport>> getStopReports({
  required List<int> deviceIds,
  required DateTime from,
  required DateTime to,
});

// Distance reports
Future<List<ReportDistance>> getDistanceReports({
  required List<int> deviceIds,
  required DateTime from,
  required DateTime to,
});

πŸ—οΈ Architecture

flutter_traccar_api/
β”œβ”€β”€ πŸ“ lib/
β”‚   β”œβ”€β”€ πŸ“„ flutter_traccar_api.dart          # Public API
β”‚   └── πŸ“ src/
β”‚       β”œβ”€β”€ πŸ“ models/                       # Data models
β”‚       β”‚   β”œβ”€β”€ πŸ“„ device.dart
β”‚       β”‚   β”œβ”€β”€ πŸ“„ position.dart
β”‚       β”‚   β”œβ”€β”€ πŸ“„ user.dart
β”‚       β”‚   └── πŸ“„ ...
β”‚       β”œβ”€β”€ πŸ“ services/                     # Core services
β”‚       β”‚   β”œβ”€β”€ πŸ“„ auth_manager.dart         # Authentication
β”‚       β”‚   β”œβ”€β”€ πŸ“„ http_service.dart         # HTTP client
β”‚       β”‚   β”œβ”€β”€ πŸ“„ cache_manager.dart        # Caching
β”‚       β”‚   β”œβ”€β”€ πŸ“„ rate_limiter.dart         # Rate limiting
β”‚       β”‚   └── πŸ“„ request_batcher.dart      # Request batching
β”‚       β”œβ”€β”€ πŸ“ exceptions/                   # Error handling
β”‚       └── πŸ“ utils/                        # Utilities
β”œβ”€β”€ πŸ“ example/                              # Example app
└── πŸ“ test/                                 # Unit tests

πŸ§ͺ Testing

Run the test suite:

# Run all tests
flutter test

# Run with coverage
flutter test --coverage

# Run specific test file
flutter test test/services/auth_manager_test.dart

🎯 Test Coverage

  • βœ… Authentication flows
  • βœ… Device management
  • βœ… Position tracking
  • βœ… Report generation
  • βœ… Error handling
  • βœ… Cache management
  • βœ… Rate limiting
  • βœ… Request batching

πŸ“‹ Requirements

Component Version
🎯 Dart SDK >=3.0.0 <4.0.0
πŸ“± Flutter >=3.0.0
πŸ–₯️ Traccar Server >=5.0

πŸ“¦ Dependencies

dependencies:
  dio: ^5.3.0                    # HTTP client
  flutter_secure_storage: ^9.0.0 # Secure storage
  shared_preferences: ^2.2.0     # Local storage
  crypto: ^3.0.3                 # Cryptographic functions
  intl: ^0.18.0                  # Internationalization

🀝 Contributing

We welcome contributions! Here's how you can help:

πŸš€ Getting Started

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/yourusername/flutter_traccar_api.git
  3. Create a feature branch: git checkout -b feature/amazing-feature
  4. Make your changes
  5. Test your changes: flutter test
  6. Commit your changes: git commit -m 'Add amazing feature'
  7. Push to the branch: git push origin feature/amazing-feature
  8. Open a Pull Request

πŸ“ Development Guidelines

  • Follow Effective Dart guidelines
  • Write tests for new features
  • Update documentation for API changes
  • Use conventional commit messages

πŸ› Reporting Issues

Found a bug? Please open an issue with:

  • πŸ“± Flutter version
  • πŸ“¦ Package version
  • πŸ” Steps to reproduce
  • πŸ“‹ Expected vs actual behavior

πŸ“š Documentation


πŸ†˜ Support

Need help? We're here for you!


πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments


Made with ❀️ by the Flutter community

⭐ Star this repo if it helped you! ⭐

Libraries

flutter_traccar_api
Flutter Traccar API Plugin