Files: - STACK.md (recommended Provider + existing packages, no new dependencies) - FEATURES.md (restructure patterns, MVVM architecture requirements) - ARCHITECTURE.md (layer separation, overlay communication, data flow) - PITFALLS.md (async lifecycle issues, GPS leaks, battery drain) - SUMMARY.md (synthesis with 4-phase roadmap recommendations) Key findings: - Stack: Provider 6.1.x + existing geolocator/overlay packages (no additions needed) - Architecture: MVVM with layer-first structure, isolate-aware overlay messaging - Critical pitfall: setState after dispose during Provider migration (audit async patterns first) Overall confidence: HIGH Estimated effort: 52-70 hours Highest risk: Phase 2 (Provider migration) and Phase 3 (Overlay refactor) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
28 KiB
Technology Stack: Flutter GPS Speedometer Restructure
Project: GPS Speedometer App Modularization Researched: 2026-02-09 Milestone Context: Restructuring existing monolithic app into modular architecture with Provider state management and Android GPS/overlay improvements
Core Principle: No New Major Dependencies
This restructure focuses on leveraging existing dependencies properly rather than introducing new packages. The stack remains deliberately minimal.
Recommended Stack
Core Framework
| Technology | Version | Purpose | Rationale |
|---|---|---|---|
| Flutter SDK | 3.24+ | Cross-platform framework | Current stable, Material 3 support |
| Dart SDK | 3.9+ | Programming language | Already in pubspec, async/null safety features |
Confidence: HIGH (Existing project versions)
State Management
| Technology | Version | Purpose | Rationale |
|---|---|---|---|
| Provider | 6.1.x | UI state management | Already in dependencies but unused. Official Flutter recommendation for simple-to-moderate state. Mature, well-documented, low boilerplate. Adequate for GPS data streaming + UI preferences. |
Why Provider over alternatives:
- NOT Riverpod: While Riverpod 3.0 is the 2026 community favorite and fixes Provider's BuildContext limitations, migration adds scope creep. Provider is sufficient for this app's complexity.
- NOT BLoC: Overkill for a single-screen app with straightforward GPS data flow.
- NOT setState alone: Current 1,075-line monolithic main.dart proves setState doesn't scale.
Confidence: HIGH (Based on Flutter official docs and 2026 state management surveys)
Location Services
| Technology | Version | Purpose | Rationale |
|---|---|---|---|
| geolocator | 14.0.2 | GPS position stream | Already integrated. Supports LocationSettings for adaptive precision control. Android 14+ compatible with FOREGROUND_SERVICE_LOCATION. |
| permission_handler | 12.0.1 | Runtime permissions | Already integrated. Handles Android location permission flows. |
Key API Pattern for Adaptive GPS Precision:
// Speed-adaptive GPS configuration
LocationSettings _getAdaptiveLocationSettings(double currentSpeed) {
// Use HIGH_ACCURACY above ~10 km/h, BALANCED below with hysteresis
final bool useHighAccuracy = currentSpeed > 3.0; // ~10.8 km/h threshold
return AndroidSettings(
accuracy: useHighAccuracy
? LocationAccuracy.high // PRIORITY_HIGH_ACCURACY (0-100m)
: LocationAccuracy.medium, // PRIORITY_BALANCED_POWER_ACCURACY (~100m)
intervalDuration: Duration(seconds: 1),
distanceFilter: useHighAccuracy ? 0 : 5, // Filter noise when slow
foregroundNotificationConfig: ForegroundNotificationConfig(
notificationText: "GPS Speedometer is tracking your location",
notificationTitle: "GPS Active",
enableWakeLock: true,
),
);
}
LocationAccuracy → Android Priority Mapping:
| Flutter Enum | Android Native | Accuracy | Power | Use Case |
|---|---|---|---|---|
LocationAccuracy.bestForNavigation |
PRIORITY_HIGH_ACCURACY |
0-100m | HIGH | Real-time driving (your HIGH state) |
LocationAccuracy.high |
PRIORITY_HIGH_ACCURACY |
0-100m | HIGH | Real-time driving (your HIGH state) |
LocationAccuracy.medium |
PRIORITY_BALANCED_POWER_ACCURACY |
~100m | MEDIUM | Low-speed / stationary (your BALANCED state) |
LocationAccuracy.low |
PRIORITY_LOW_POWER |
100-500m | LOW | Background tracking (not needed) |
LocationAccuracy.lowest |
PRIORITY_PASSIVE |
500m+ | MINIMAL | Piggyback only (not needed) |
Important: Use AndroidSettings.intervalDuration to control update frequency. The deprecated getCurrentPosition() parameters (desiredAccuracy, timeLimit) were replaced with LocationSettings in geolocator 6.0+.
Confidence: HIGH (Official geolocator docs and Android LocationRequest API)
Android Overlay Window
| Technology | Version | Purpose | Rationale | Status |
|---|---|---|---|---|
| flutter_overlay_window | 0.5.0 | System-level overlay | Already integrated. Known reliability issues: bidirectional communication unstable, overlay positioning poor, performance impact on main app. | PROBLEMATIC |
Critical Finding: Overlay Communication is Fundamentally Flawed
The existing flutter_overlay_window implementation has documented reliability problems:
- Data Sync Issues: GitHub issue #115 documents "can't display my data on the overlay window" - matches project's "seriously buggy" assessment.
- Hardware Rendering Glitches: Flutter 3.27+ causes graphic glitches with overlays on Samsung devices, requiring "Disable HW Overlays" workaround.
- Android Version Inconsistency: Android 11+ shows notification bubbles instead of overlays on some configurations.
Recommended Strategy: Isolate and Stabilize, Not Replace
WHY NOT switch packages:
- system_alert_window: Same underlying Android limitations, different API. No reliability gain.
- flutter_overlay_window_plus: "Improved API" but no evidence of fixing fundamental IPC issues.
- Native Android implementation: Massive scope creep, requires platform channel expertise.
INSTEAD: Architectural Isolation Pattern
Main App (Foreground)
├── GPS Hardware ← Single source of truth
├── GpsDataManager (Singleton) ← Processes data
├── UI Widgets (Consumer) ← Display main UI
└── Overlay Messaging (Fire-and-forget) ← One-way updates only
Overlay Window (Background isolate)
├── Receives messages via FlutterOverlayWindow.overlayListener
├── Displays last-received GPS data (read-only)
└── Close button → Sends termination signal
Key Pattern: One-Way Data Flow with Defensive Fallbacks
// In main app: Fire-and-forget messaging
void _sendGpsDataToOverlay(ProcessedGpsData data) {
if (_isOverlayActive) {
try {
FlutterOverlayWindow.shareData({
'speed': data.displaySpeed,
'unit': _currentUnit.symbol,
'heading': data.displayHeading,
});
} catch (e) {
// Fail silently - overlay updates are nice-to-have
debugPrint('Overlay message failed: $e');
}
}
}
// In overlay: Defensive data handling
@override
Widget build(BuildContext context) {
return StreamBuilder<dynamic>(
stream: FlutterOverlayWindow.overlayListener,
initialData: {'speed': '--', 'unit': 'km/h', 'heading': 'N/A'},
builder: (context, snapshot) {
final data = snapshot.data ?? {};
return Text('${data['speed'] ?? '--'} ${data['unit'] ?? ''}');
},
);
}
What NOT to do:
- ❌ Bidirectional communication (overlay → main app requests)
- ❌ Shared mutable state between isolates
- ❌ Complex UI in overlay (keep it minimal: speed + close button)
- ❌ Rely on overlay for critical functionality
Confidence: MEDIUM (Based on GitHub issues and pub.dev package page)
Power Management
| Technology | Version | Purpose | Rationale |
|---|---|---|---|
| wakelock_plus | 1.2.8 | Keep screen on | Already integrated. Prevents screen sleep during active navigation. |
Power Optimization Pattern for Mixed-Use (Car + Battery):
// Conditional GPS tracking based on overlay visibility
void _handleOverlayStateChange(bool overlayVisible) {
if (overlayVisible) {
// Start background GPS with BALANCED accuracy
_gpsManager.startBackgroundTracking(LocationAccuracy.medium);
WakelockPlus.enable(); // Prevent sleep
} else {
// Stop GPS when overlay hidden + app backgrounded
if (_appLifecycleState == AppLifecycleState.paused) {
_gpsManager.stopTracking();
WakelockPlus.disable();
}
}
}
Android GPS Power Best Practices:
- Use FusedLocationProvider: Intelligently combines GPS, Wi-Fi, cell towers. geolocator uses this by default.
- Maximize intervalDuration: Pass largest possible interval value for foreground use cases (1-2 seconds acceptable).
- Batch updates: Use
AndroidSettings.maxUpdateDelayMillis()to batch multiple location updates (delay 3-5x the interval). - Switch to BALANCED_POWER_ACCURACY when stationary: Reserve HIGH_ACCURACY for movement.
- Stop updates when unnecessary: Remove location listeners in
onPause(), restore inonResume().
Why this matters for GPS speedometer:
- Car use: Plugged in, no battery concern → HIGH_ACCURACY always acceptable
- Battery cycling (parking → driving): Must stop GPS when parked, restart on movement
- Overlay visible = user expects real-time data = justify GPS power drain
Confidence: HIGH (Based on Android battery optimization docs and Google Maps SDK power guide)
Architecture Stack
Recommended Pattern: MVVM (Simplified)
Based on Flutter official architecture guide:
UI Layer:
├── Views (Widgets) ← Minimal logic, display only
└── View Models (ChangeNotifier) ← UI state + user interaction commands
Data Layer:
├── Repositories ← Data source of truth (GPS, settings)
└── Services ← External API wrappers (Geolocator, SharedPreferences)
No Domain Layer Needed: Use-cases/interactors are overkill for single-screen GPS app. Only add domain layer if merging multiple complex repositories.
Component Boundaries:
| Component | Responsibility | Communicates With |
|---|---|---|
| SpeedometerScreen (View) | Widget tree, user input handling | SpeedometerViewModel only |
| SpeedometerViewModel (ChangeNotifier) | UI state (theme, unit, display mode), user commands | GpsRepository, SettingsRepository |
| GpsRepository | GPS data stream, speed/heading processing | GpsService, ProcessedGpsData model |
| SettingsRepository | Persistent settings (theme, unit) | SettingsService (SharedPreferences) |
| GpsService | Geolocator API wrapper, permission handling | Android GPS hardware |
| OverlayMessenger (Optional service) | One-way data to overlay isolate | flutter_overlay_window |
Critical: Existing GpsDataManager Must Be Refactored
Current implementation mixes responsibilities:
- ✅ Correct: Singleton pattern, broadcast stream, stale data detection
- ❌ Wrong: Directly processes UI display logic (
displaySpeed,displayHeading) - ❌ Wrong: Hardcoded m/s → display conversion (should be repository concern)
Refactor Strategy:
// BEFORE (Current GpsDataManager) - Service with UI logic
class GpsDataManager {
ProcessedGpsData _currentData; // Contains displaySpeed, displayHeading
String getFormattedSpeed(SpeedUnit unit) { /* UI formatting */ }
}
// AFTER (Clean Architecture)
// 1. GpsService (Pure data layer) - Just wraps Geolocator
class GpsService {
Stream<Position> get positionStream => Geolocator.getPositionStream(...);
static Future<bool> requestPermissions() => ...;
}
// 2. GpsRepository (Data processing) - Converts Position → domain model
class GpsRepository {
Stream<GpsData> get dataStream => _gpsService.positionStream
.map((position) => GpsData(
speedMps: position.speed,
headingDegrees: position.heading,
isValid: position.speed.isFinite,
));
}
// 3. SpeedometerViewModel (UI logic) - Formats for display
class SpeedometerViewModel extends ChangeNotifier {
String get displaySpeed {
final data = _gpsRepository.currentData;
if (!data.isValid || data.speedMps < 0.3) return '--';
return _currentUnit.convert(data.speedMps).toStringAsFixed(1);
}
}
Confidence: HIGH (Official Flutter architecture guide)
Directory Structure: Layer-First (Recommended for This Project)
Feature-first vs Layer-first decision:
| Approach | Best For | This Project? |
|---|---|---|
| Feature-first | Multi-feature apps (e.g., e-commerce: cart, checkout, profile) | ❌ No - Single-screen GPS app |
| Layer-first | Apps with few features but complex layers | ✅ Yes - One feature, multiple concerns (GPS, overlay, settings) |
Recommended Structure:
lib/
├── main.dart # App entry point + overlay entry point
├── screens/ # UI Layer - Views only
│ ├── speedometer_screen.dart # Main screen widget tree
│ └── overlay_screen.dart # Overlay isolate UI (if separated)
├── viewmodels/ # UI Layer - View models (ChangeNotifier)
│ └── speedometer_viewmodel.dart
├── widgets/ # Reusable UI components
│ ├── speed_display.dart # Digital display widget
│ ├── gauge_widget.dart # Analog gauge widget
│ └── metrics_panel.dart # Optional metrics display
├── repositories/ # Data Layer - Data processing
│ ├── gps_repository.dart # GPS data stream + processing
│ └── settings_repository.dart # Persistent settings (theme, unit)
├── services/ # Data Layer - External API wrappers
│ ├── gps_service.dart # Geolocator wrapper
│ ├── settings_service.dart # SharedPreferences wrapper
│ └── overlay_messenger.dart # One-way overlay communication (optional)
├── models/ # Data models (pure data classes)
│ ├── gps_data.dart # Domain model for GPS data
│ └── app_settings.dart # Domain model for settings
├── utils/ # Pure functions, constants
│ ├── speed_units.dart # SpeedUnit enum + conversion logic
│ └── color_themes.dart # Theme definitions
└── constants/ # App-wide constants
└── app_constants.dart # Timeouts, thresholds, etc.
Migration Path from Current Monolith:
- Phase 1: Extract models (
gps_data.dart,app_settings.dart) - Phase 2: Create services (wrap existing
GpsService, createSettingsService) - Phase 3: Create repositories (move business logic from
GpsDataManager) - Phase 4: Create viewmodel (extract state from
_SpeedometerScreenState) - Phase 5: Refactor screen to pure widget (remove setState, use Consumer)
What NOT to do:
- ❌ Create
helpers/orutils/catch-all folders for business logic - ❌ Mix UI and business logic in viewmodels
- ❌ Put repositories in
providers/folder (userepositories/) - ❌ Create deep feature hierarchies (
features/speedometer/screens/main/widgets/...)
Confidence: HIGH (Based on Flutter project structure guide and official architecture docs)
Provider Patterns: Performance Best Practices
ChangeNotifier Usage
Core Pattern:
class SpeedometerViewModel extends ChangeNotifier {
final GpsRepository _gpsRepository;
final SettingsRepository _settingsRepository;
StreamSubscription<GpsData>? _gpsSubscription;
SpeedometerViewModel(this._gpsRepository, this._settingsRepository) {
_gpsSubscription = _gpsRepository.dataStream.listen(_onGpsUpdate);
_loadSettings();
}
void _onGpsUpdate(GpsData data) {
// Update internal state
_currentGpsData = data;
notifyListeners(); // Trigger UI rebuild
}
@override
void dispose() {
_gpsSubscription?.cancel();
super.dispose();
}
}
Critical Performance Rules:
-
Place Consumer deep in widget tree:
// ❌ BAD - Rebuilds entire screen return Consumer<SpeedometerViewModel>( builder: (context, vm, child) => Scaffold(...), ); // ✅ GOOD - Rebuilds only speed text return Scaffold( body: Column( children: [ StaticHeader(), Consumer<SpeedometerViewModel>( builder: (context, vm, child) => Text(vm.displaySpeed), ), ], ), ); -
Use
listen: falsewhen not subscribing:// ✅ In event handlers (button presses) void _onThemeButtonPressed() { final vm = Provider.of<SpeedometerViewModel>(context, listen: false); vm.nextTheme(); // Just calling a command, not listening to changes } // Or use context.read (same thing) void _onThemeButtonPressed() { context.read<SpeedometerViewModel>().nextTheme(); } -
Use
childparameter for static subtrees:Consumer<SpeedometerViewModel>( child: ExpensiveStaticWidget(), // Built once, passed to builder builder: (context, vm, staticChild) => Column( children: [ Text(vm.displaySpeed), // Rebuilds staticChild!, // Never rebuilds ], ), ); -
Use Selector for granular rebuilds:
// Only rebuilds when displaySpeed changes, not on theme/unit changes Selector<SpeedometerViewModel, String>( selector: (context, vm) => vm.displaySpeed, builder: (context, displaySpeed, child) => Text(displaySpeed), ); -
Always call
notifyListeners()after state changes:void setUnit(SpeedUnit unit) { _currentUnit = unit; notifyListeners(); // Critical - UI won't update without this } -
NEVER use
context.read()inside build():// ❌ BAD - Won't rebuild on changes @override Widget build(BuildContext context) { final vm = context.read<SpeedometerViewModel>(); return Text(vm.displaySpeed); // Stale data! } // ✅ GOOD - Subscribes to changes @override Widget build(BuildContext context) { final vm = context.watch<SpeedometerViewModel>(); return Text(vm.displaySpeed); }
Provider Setup in main.dart:
void main() {
runApp(
MultiProvider(
providers: [
// Services (no state, just API wrappers)
Provider(create: (_) => GpsService()),
Provider(create: (_) => SettingsService()),
// Repositories (depend on services)
ProxyProvider<GpsService, GpsRepository>(
update: (_, gpsService, __) => GpsRepository(gpsService),
),
ProxyProvider<SettingsService, SettingsRepository>(
update: (_, settingsService, __) => SettingsRepository(settingsService),
),
// ViewModels (depend on repositories)
ChangeNotifierProxyProvider2<GpsRepository, SettingsRepository, SpeedometerViewModel>(
create: (_) => SpeedometerViewModel(
Provider.of<GpsRepository>(_, listen: false),
Provider.of<SettingsRepository>(_, listen: false),
),
update: (_, gpsRepo, settingsRepo, vm) =>
vm ?? SpeedometerViewModel(gpsRepo, settingsRepo),
),
],
child: const SpeedoApp(),
),
);
}
Confidence: HIGH (Based on official Flutter Provider docs and Provider performance guide)
Installation & Dependency Changes
No new packages needed. All dependencies already in pubspec.yaml.
Current Dependencies (Keep As-Is)
dependencies:
flutter:
sdk: flutter
geolocator: ^14.0.2 # Already integrated
permission_handler: ^12.0.1 # Already integrated
wakelock_plus: ^1.2.8 # Already integrated
flutter_overlay_window: ^0.5.0 # Already integrated (problematic but keep)
bg_launcher: ^0.1.0 # Already integrated
# provider: ^6.1.x # MISSING - Add this line
Required Addition
flutter pub add provider
Dev Dependencies (Keep As-Is)
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter_launcher_icons: "^0.14.4"
What NOT to add:
- ❌ Riverpod (scope creep, Provider is sufficient)
- ❌ get_it (dependency injection - Provider handles this)
- ❌ freezed (immutable models - overkill for simple data classes)
- ❌ Alternative overlay packages (same limitations as current)
Anti-Patterns: What NOT to Do
1. Don't Over-Abstract
// ❌ BAD - Premature abstraction
abstract class LocationDataSource {
Stream<Position> getPositionStream();
}
class GeolocatorDataSource implements LocationDataSource { ... }
// ✅ GOOD - Direct service wrapper
class GpsService {
Stream<Position> get positionStream => Geolocator.getPositionStream(...);
}
Why: You're not swapping out Geolocator. YAGNI principle applies.
2. Don't Mix UI Logic in Repositories
// ❌ BAD - Repository formats for UI
class GpsRepository {
String get displaySpeed => _formatForUi(_currentSpeed);
}
// ✅ GOOD - Repository provides domain data
class GpsRepository {
double get speedMps => _currentSpeed;
}
class SpeedometerViewModel {
String get displaySpeed => _formatForUi(_gpsRepository.speedMps);
}
Why: Repositories are reusable across features. UI formatting is view-specific.
3. Don't Create God ViewModels
// ❌ BAD - Single viewmodel manages everything
class AppViewModel extends ChangeNotifier {
GpsData gpsData;
AppSettings settings;
bool isOverlayActive;
// ... 50 more properties
}
// ✅ GOOD - Separate concerns
class SpeedometerViewModel extends ChangeNotifier { /* GPS + UI state */ }
class SettingsViewModel extends ChangeNotifier { /* Settings only */ }
Why: For single-screen app, one viewmodel is OK. But separate GPS from settings concerns.
4. Don't Use Dynamic Accuracy Switching Without Hysteresis
// ❌ BAD - Constantly switches accuracy as speed fluctuates
LocationSettings _getSettings(double speed) {
return AndroidSettings(
accuracy: speed > 3.0
? LocationAccuracy.high // 10.8 km/h
: LocationAccuracy.medium,
);
}
// ✅ GOOD - Hysteresis prevents thrashing
LocationSettings _getSettings(double speed, bool currentlyHighAccuracy) {
final threshold = currentlyHighAccuracy ? 2.5 : 3.5; // 9-12.6 km/h range
return AndroidSettings(
accuracy: speed > threshold
? LocationAccuracy.high
: LocationAccuracy.medium,
);
}
Why: GPS speed fluctuates ±0.5 m/s. Without hysteresis, you'll rapidly switch accuracy modes, wasting battery and causing UI jank.
5. Don't Rely on Overlay for Critical Features
// ❌ BAD - Overlay failure breaks app
void _sendGpsUpdate(GpsData data) {
if (_isOverlayActive) {
FlutterOverlayWindow.shareData(data.toJson()); // May throw
} else {
throw Exception('Overlay not active!'); // App crashes
}
}
// ✅ GOOD - Overlay is optional enhancement
void _sendGpsUpdate(GpsData data) {
if (_isOverlayActive) {
try {
FlutterOverlayWindow.shareData(data.toJson());
} catch (e) {
debugPrint('Overlay update failed, continuing: $e');
}
}
// Main app always works, overlay is bonus
}
Why: Overlay communication is unreliable. Main app must function perfectly without it.
Testing Strategy
Unit Test Priorities
- Speed unit conversions (
speed_units.dart) - Pure functions, easy to test - GPS data processing (
GpsRepository) - Mock Position stream - Settings persistence (
SettingsRepository) - Mock SharedPreferences - ViewModel commands (
SpeedometerViewModel) - Mock repositories
Widget Test Priorities
- Speed display updates - Verify Consumer rebuilds on GPS changes
- Theme switching - Verify UI updates on theme change
- Unit switching - Verify speed conversion displayed correctly
Integration Test Priorities
- GPS permission flow - Verify permission request → GPS stream start
- Overlay launch/close - Verify overlay lifecycle doesn't crash main app
What NOT to test:
- ❌ Geolocator library internals (trust the library)
- ❌ Overlay message delivery (too unreliable, mock it)
- ❌ Android system behaviors (permission dialogs, etc.)
Sources
Official Documentation
- Flutter App Architecture Guide - MVVM, layer separation
- Flutter State Management: Simple (Provider) - Official Provider guidance
- Geolocator Flutter Package - GPS API documentation
- Android Location Request Updates - Native Android GPS API
- Android Location Battery Optimization - Power consumption best practices
Community Resources
- Flutter Project Structure: Feature-first or Layer-first? - Directory structure guidance
- Best Flutter State Management Libraries 2026 - Provider vs Riverpod comparison
- Flutter Provider State Management for Optimal Performance - Performance patterns
Package Documentation
- flutter_overlay_window pub.dev - Overlay API reference
- flutter_overlay_window GitHub Issues - Known reliability problems
- LocationAccuracy enum API - Android mapping table
Architecture References
- Architecture recommendations and resources - Flutter team's architectural guidance
- Common architecture concepts - Layer definitions
Confidence Assessment
| Area | Confidence | Rationale |
|---|---|---|
| Provider patterns | HIGH | Official Flutter docs, mature package (6.1.x), well-documented |
| MVVM architecture | HIGH | Official Flutter architecture guide 2026 |
| Geolocator adaptive GPS | HIGH | Official Android docs + geolocator 14.0.2 API verified |
| Android power optimization | HIGH | Official Android developer docs for 2026 |
| Overlay reliability | MEDIUM | GitHub issues + community reports confirm problems, but no authoritative solution found |
| Directory structure | HIGH | Layer-first appropriate for single-screen app per community best practices |
Open Questions / Phase-Specific Research Needed
-
Overlay Performance Impact: Quantify actual battery/CPU impact of running overlay isolate on various Android versions. May need device-specific testing.
-
GPS Accuracy Hysteresis Tuning: Optimal speed thresholds for HIGH ↔ BALANCED transitions depend on real-world testing. Starting recommendation: 2.5-3.5 m/s (~9-12.6 km/h) with 1.0 m/s deadband.
-
Android 14+ Foreground Service Policies: Verify FOREGROUND_SERVICE_LOCATION permission behavior on Android 14+ devices. May require additional AndroidManifest.xml configuration beyond current setup.
-
Provider Memory Leaks: Verify ChangeNotifier disposal in overlay isolate. Isolates don't share memory, so dispose() semantics may differ. Test with DevTools memory profiler.
Next Steps: Feed this STACK.md into roadmap creation to structure phases around:
- Provider migration (extract viewmodels)
- Repository pattern implementation (GpsRepository, SettingsRepository)
- Adaptive GPS precision (speed-based LocationSettings)
- Overlay isolation (one-way messaging, defensive error handling)
- Power optimization (lifecycle-aware GPS control)