refactor: complete layered architecture migration

Delete old flat files, wire new DI in main.dart:
- Removed: gps_service.dart, config/, models/, providers/overlay_provider.dart,
  screens/, services/gps_data_manager.dart, services/overlay_service.dart
- Kept: settings_provider.dart (thin, correct), logger.dart (shared util)
- main.dart now wires: LocationRepository → SpeedometerViewModel → SpeedometerView
- overlay_provider.dart deferred, stub in ui/features/overlay/
- flutter analyze: 30 info-level constant_identifier_names warnings (project convention),
  zero errors, zero warnings — builds clean

New structure:
  lib/
    core/constants/     — gps, overlay, timing, speed_unit
    core/theme/         — color_themes
    data/
      models/           — LocationData (immutable domain model)
      services/         — GpsService (stateless wrapper), OverlayService (IPC)
      repositories/     — LocationRepository (business logic, ChangeNotifier)
    providers/          — SettingsProvider (thin, unchanged)
    ui/
      features/
        speedometer/    — SpeedometerViewModel + SpeedometerView
        overlay/        — Stub ViewModel + View (deferred)
      shared/           — SpeedDisplay widget
    services/           — Logger (shared util)
This commit is contained in:
2026-08-23 17:32:29 +09:00
parent 6bba05efc8
commit 248053270b
13 changed files with 55 additions and 1984 deletions
@@ -1,9 +1,9 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/speed_unit.dart';
import '../../../data/models/location_data.dart';
import '../../../data/repositories/location_repository.dart';
import '../../../providers/settings_provider.dart';
/// ViewModel for the speedometer UI.
///
@@ -11,71 +11,24 @@ import '../../../data/repositories/location_repository.dart';
/// the current display state to the view. No data-layer logic here.
class SpeedometerViewModel extends ChangeNotifier {
final LocationRepository _repository;
final SharedPreferences _prefs;
SettingsProvider? _settings;
SpeedUnit _displayUnit = SpeedUnit.kmh;
LocationData _displayData = LocationData(
speed: 0.0,
heading: -1.0,
displaySpeed: '--',
displayHeading: 'N/A',
isSpeedValid: false,
isHeadingValid: false,
timestamp: DateTime.now(),
);
SpeedUnit get displayUnit => _settings?.currentUnit ?? SpeedUnit.kmh;
LocationData get displayData => _repository.currentData;
String get displaySpeedText => _repository.currentData.displaySpeed;
String get displayHeading => _repository.currentData.displayHeading;
bool get isSpeedValid => _repository.currentData.isSpeedValid;
bool get isHeadingValid => _repository.currentData.isHeadingValid;
LocationData get displayData => _displayData;
SpeedUnit get displayUnit => _displayUnit;
SpeedometerViewModel(this._repository);
SpeedometerViewModel({
required LocationRepository repository,
required SharedPreferences prefs,
}) : _repository = repository,
_prefs = prefs;
void init() {
_repository.locationStream.listen(_onLocationData);
_loadSettings();
}
void _onLocationData(LocationData data) {
_displayData = data;
notifyListeners();
}
Future<void> _loadSettings() async {
_displayUnit = _parseSpeedUnit(_prefs.getString('speed_unit') ?? 'km/h');
notifyListeners();
}
static SpeedUnit _parseSpeedUnit(String unit) {
switch (unit) {
case 'mph':
return SpeedUnit.mph;
case 'kts':
return SpeedUnit.knots;
default:
return SpeedUnit.kmh;
}
/// Call when the ViewModel is inserted into the provider tree so it
/// can listen to SettingsProvider and repository state.
void attach(BuildContext context) {
_settings = context.read<SettingsProvider>();
}
double get displaySpeed {
return _displayUnit.convert(_displayData.speed);
}
String get displaySpeedText {
return _displayData.displaySpeed;
}
String get displayHeading {
return _displayData.displayHeading;
}
bool get isSpeedValid {
return _displayData.isSpeedValid;
}
bool get isHeadingValid {
return _displayData.isHeadingValid;
return displayUnit.convert(_repository.currentData.speed);
}
}