From f458b62eea4dccce03a2d6f184c979a74e3f0613 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Tue, 10 Feb 2026 22:03:13 +0900 Subject: [PATCH] chore(02-01): add provider dependencies and refactor GpsDataManager - Added provider ^6.1.2 and shared_preferences ^2.3.4 to pubspec.yaml - Removed singleton pattern from GpsDataManager (deleted _instance and instance getter) - Changed GpsDataManager._internal() to public GpsDataManager() constructor - Extended ChangeNotifier to enable reactive state updates - Added _isDisposed guard field to prevent disposed notifications - Updated _updateData() to call notifyListeners() after stream emission - Added _isDisposed guards to _onGpsError() and _handleStaleDataTimeout() - Updated dispose() to set _isDisposed, call super.dispose() last, removed _isInitialized reset --- lib/services/gps_data_manager.dart | 17 +++++++++++------ pubspec.yaml | 6 ++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/services/gps_data_manager.dart b/lib/services/gps_data_manager.dart index 2b8c1ce..7838d3b 100644 --- a/lib/services/gps_data_manager.dart +++ b/lib/services/gps_data_manager.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:geolocator/geolocator.dart'; import '../gps_service.dart'; import '../config/speed_unit.dart'; @@ -6,11 +7,8 @@ import '../config/gps_constants.dart'; import '../models/processed_gps_data.dart'; import 'logger.dart'; -class GpsDataManager { - static GpsDataManager? _instance; - static GpsDataManager get instance => _instance ??= GpsDataManager._internal(); - - GpsDataManager._internal(); +class GpsDataManager extends ChangeNotifier { + GpsDataManager(); StreamSubscription? _gpsSubscription; final StreamController _dataController = @@ -26,6 +24,7 @@ class GpsDataManager { ); Timer? _staleDataTimer; + bool _isDisposed = false; // Public stream for UI components to subscribe to Stream get dataStream => _dataController.stream; @@ -157,6 +156,7 @@ class GpsDataManager { } void _onGpsError(dynamic error) { + if (_isDisposed) return; // Handle GPS errors gracefully final errorString = error.toString().toLowerCase(); if (!errorString.contains('timeout') && !errorString.contains('temporarily')) { @@ -175,6 +175,7 @@ class GpsDataManager { } void _handleStaleDataTimeout() { + if (_isDisposed) return; // Avoid overriding explicit GPS error states or repeated stale notifications final displayText = _currentData.displaySpeed; if (displayText == '--' || displayText.startsWith('GPS') || displayText == 'NO PERM') { @@ -198,9 +199,11 @@ class GpsDataManager { } void _updateData(ProcessedGpsData newData) { + if (_isDisposed) return; Logger.debug('Broadcasting data: ${newData.displaySpeed} ${newData.displayHeading}', 'GpsDataManager'); _currentData = newData; _dataController.add(newData); + notifyListeners(); Logger.debug('Data broadcast complete', 'GpsDataManager'); } @@ -212,7 +215,9 @@ class GpsDataManager { return convertedSpeed < 1.0 ? '--' : convertedSpeed.toStringAsFixed(1); } + @override void dispose() { + _isDisposed = true; Logger.info('Disposing GPS manager...', 'GpsDataManager'); if (_gpsSubscription != null) { Logger.debug('Cancelling GPS subscription...', 'GpsDataManager'); @@ -223,7 +228,7 @@ class GpsDataManager { _staleDataTimer?.cancel(); _staleDataTimer = null; _dataController.close(); - _isInitialized = false; Logger.info('GPS manager disposed', 'GpsDataManager'); + super.dispose(); } } diff --git a/pubspec.yaml b/pubspec.yaml index 47ccff7..9f5c551 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,12 @@ dependencies: # Bring app to foreground from background/overlay bg_launcher: ^0.1.0 + # State management + provider: ^6.1.2 + + # Persistent settings storage + shared_preferences: ^2.3.4 + dev_dependencies: flutter_test: