fix: critical bugs preventing location reporting to server

- Send config to native before starting tracking (SharedPreferences sync)
- Register ConnectivityReceiver for network state detection
- Fix Settings number fields using TextEditingControllers
- Cancel stream subscription on widget dispose (memory leak)
- Replace WorkManager with AlarmManager for heartbeat
- Add log cleanup for logs older than 24 hours
- Change HTTP method from GET to POST

These fixes resolve the 'device always offline' issue where:
1. Config was not being sent to native service
2. ConnectivityReceiver was never registered
3. Settings number fields were not saving
4. Heartbeat never fired due to WorkManager process isolation
5. Server expected POST not GET
This commit is contained in:
fiatcode 2026-04-30 15:38:24 +07:00
parent 3ae8bf00c1
commit bbd51d1c35
No known key found for this signature in database
10 changed files with 1200 additions and 167 deletions

View file

@ -1,5 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:traccar_client/bridge/location_bridge.dart';
import 'package:traccar_client/preferences.dart';
import 'package:traccar_client/settings_screen.dart';
import 'package:traccar_client/status_screen.dart';
@ -11,6 +14,7 @@ class MainScreen extends StatefulWidget {
}
class _MainScreenState extends State<MainScreen> {
StreamSubscription<Map<String, dynamic>>? _locationSubscription;
bool _isTracking = false;
String _lastLat = '--';
String _lastLon = '--';
@ -23,6 +27,12 @@ class _MainScreenState extends State<MainScreen> {
_initLocationStream();
}
@override
void dispose() {
_locationSubscription?.cancel();
super.dispose();
}
Future<void> _initLocationStream() async {
final status = await LocationBridge.getStatus();
if (mounted) {
@ -49,7 +59,7 @@ class _MainScreenState extends State<MainScreen> {
});
}
LocationBridge.locationUpdates.listen((location) {
_locationSubscription = LocationBridge.locationUpdates.listen((location) {
if (mounted) {
setState(() {
_lastLat = location['latitude']?.toStringAsFixed(4) ?? '--';
@ -72,17 +82,39 @@ class _MainScreenState extends State<MainScreen> {
}
Future<void> _toggleTracking() async {
if (_isTracking) {
await LocationBridge.stopTracking();
} else {
await LocationBridge.startTracking();
}
await Future.delayed(const Duration(milliseconds: 500));
final status = await LocationBridge.getStatus();
if (mounted) {
setState(() {
_isTracking = status?['isTracking'] == true;
final previousState = _isTracking;
final newState = !_isTracking;
setState(() {
_isTracking = newState;
});
bool success;
if (newState) {
success = await LocationBridge.startTracking(config: {
'serverUrl': Preferences.serverUrl,
'deviceId': Preferences.deviceId,
'password': Preferences.password,
'accuracy': Preferences.accuracy,
'distanceFilter': Preferences.distanceFilter,
'interval': Preferences.interval,
'heartbeat': Preferences.heartbeat,
'offlineBuffer': Preferences.offlineBuffer,
'stopDetection': Preferences.stopDetection,
});
} else {
success = await LocationBridge.stopTracking();
}
if (!success && mounted) {
setState(() {
_isTracking = previousState;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(previousState ? 'Failed to stop tracking' : 'Failed to start tracking'),
),
);
}
}
@ -169,9 +201,10 @@ class _MainScreenState extends State<MainScreen> {
SnackBar(
content: Text(
success
? 'Location reported'
: 'Failed to report location',
? 'Location sent to server'
: 'Failed - check logs',
),
duration: Duration(seconds: 2),
),
);
}