flutter_traccar_api 0.1.0
flutter_traccar_api: ^0.1.0 copied to clipboard
A comprehensive Dart package for integrating with Traccar GPS tracking server. Provides authentication, device management, position tracking, real-time WebSocket updates, and reporting capabilities.
π Flutter Traccar API #
A powerful, feature-rich Flutter package for seamless integration with Traccar GPS tracking servers
π Documentation β’ π Quick Start β’ π‘ Examples β’ π€ Contributing
β¨ Features #
π Authentication & Security #
π± Device Management #
|
π Position Tracking #
π Advanced Reporting #
π Real-time WebSocket #
|
π 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 #
- Fork the repository
- Clone your fork:
git clone https://github.com/yourusername/flutter_traccar_api.git
- Create a feature branch:
git checkout -b feature/amazing-feature
- Make your changes
- Test your changes:
flutter test
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- 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 #
- π API Documentation
- π― Traccar API Reference
- π± Flutter Documentation
π Support #
Need help? We're here for you!
- π¬ GitHub Discussions
- π Issue Tracker
- π§ Email Support
- π¬ Discord Community
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
π Acknowledgments #
- π Traccar - Open source GPS tracking system
- π± Flutter Team - Amazing cross-platform framework
- π― Dart Team - Powerful programming language
- π€ All our contributors
Made with β€οΈ by the Flutter community
β Star this repo if it helped you! β