From 71b7611a48cd7f5d5face6118fa0a3b08077989d Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Thu, 21 Aug 2025 01:58:46 +0900 Subject: [PATCH] Implement v1.1 enhancements: auto-rotate, interactive gauge, and extended units MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Features added: - Auto-rotate support for portrait/landscape orientations - Interactive gauge: tap to switch between digital and analog modes - Extended speed units: added knots with quick selector buttons - New SpeedUnitSelector widget for convenient unit switching Known issues identified: - 10 package dependencies need updates to latest compatible versions - Orientation switching needs to override system auto-rotate toggle - Background color customization moved back to TODO (build issues) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .claude/settings.local.json | 6 +- CLAUDE.md | 22 +++++-- README.md | 16 ++++-- .../reports/problems/problems-report.html | 2 +- lib/main.dart | 7 ++- lib/providers/settings_provider.dart | 24 ++++++-- lib/screens/settings_screen.dart | 12 ++++ lib/screens/speedometer_screen.dart | 41 +++++++++---- lib/widgets/speed_unit_selector.dart | 57 +++++++++++++++++++ 9 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 lib/widgets/speed_unit_selector.dart diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d4272f2..42bc29a 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -8,7 +8,11 @@ "Bash(flutter pub:*)", "Bash(mkdir:*)", "Bash(flutter analyze:*)", - "Bash(flutter build:*)" + "Bash(flutter build:*)", + "Bash(git push:*)", + "Bash(cp:*)", + "Bash(flutter:*)", + "Bash(powershell:*)" ], "defaultMode": "acceptEdits" } diff --git a/CLAUDE.md b/CLAUDE.md index a87fcac..8b68af5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,8 +3,8 @@ ## Project Overview A customizable Flutter GPS speedometer app targeting Android devices, emphasizing modularity and user customization. -## Current Status: v1.0 Complete  -**Fully functional GPS speedometer with all core requirements implemented.** +## Current Status: v1.1 Complete  +**Enhanced GPS speedometer with improved user experience and customization options.** ### Architecture Implemented - **State Management:** Provider pattern @@ -15,7 +15,7 @@ A customizable Flutter GPS speedometer app targeting Android devices, emphasizin ### Core Features Completed -  Real-time GPS speed tracking with accuracy monitoring --  Customizable speed units (km/h ” mph) with persistent storage +-  Customizable speed units (km/h � mph) with persistent storage -  Dual display styles: Digital speedometer + Analog gauge -  Dark/Light theme support with system integration -  Optional metrics panel: coordinates, distance, trip time, accuracy @@ -31,12 +31,22 @@ Architecture: Provider + Consumer pattern for reactive state management Permissions: Location services with graceful degradation ``` -## TODO - Future Improvements = +## Recent Updates (v1.1) + +### ✅ Completed Features +- [x] **Portrait/Landscape Support**: Automatic orientation detection implemented +- [x] **Interactive Gauge**: Click gauge face to switch digital/analog modes +- [x] **Extended Speed Units**: Added knots support with quick selector buttons + +## TODO - Future Improvements + +### Critical Issues +- [ ] **Dependency Updates**: Update 10 packages to latest compatible versions +- [ ] **Orientation Override**: Fix auto-rotate to work regardless of system toggle status ### UI/UX Enhancements -- [ ] **Portrait/Landscape Support**: Automatic orientation detection (ignore system auto-rotate toggle) +- [ ] **Background Customization**: Custom background color selection - [ ] **Interface Rearrangement**: User-defined layout customization -- [ ] **Interactive Gauge**: Click gauge face to switch digital/analog modes - [ ] **Settings Menu Refurbishment**: More intuitive interactions and better UX ### Technical Improvements diff --git a/README.md b/README.md index 0d39f1a..bc44a16 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ A customizable Flutter speedometer app using GPS location services to display re - Real-time speed display using GPS - High-precision location tracking - Works offline (no internet connection required) -- Portrait orientation for mobile phones +- Automatic portrait/landscape orientation support ### Customizable Settings -- **Speed Units**: mph, km/h (expandable for more units) -- **Display Style**: Digital speedometer, analog gauge (expandable for more styles) +- **Speed Units**: mph, km/h, knots (with convenient unit selector buttons) +- **Display Style**: Digital speedometer, analog gauge (tap gauge to switch) - **Theme**: Light/Dark mode (expandable for custom themes) - **Metrics Display**: Toggle additional metrics on/off - Current location coordinates @@ -23,11 +23,17 @@ A customizable Flutter speedometer app using GPS location services to display re ### Technical Specifications - Target Platform: Android (primary) -- Screen Orientation: Portrait (locked) -- Target Device: Mobile phones +- Screen Orientation: Auto-rotate (portrait/landscape) +- Target Device: Mobile phones and tablets - GPS Requirements: Location permissions required - Offline Capability: Full functionality without internet +### New Features in v1.1 +- **Auto-rotate Support**: App now supports both portrait and landscape orientations +- **Interactive Gauge**: Tap the speedometer to switch between digital and analog modes +- **Extended Units**: Added knots as a speed unit option +- **Unit Selector**: Quick access unit selection buttons next to the speedometer + ## Architecture Design The app is designed with customization and extensibility in mind: diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index d3f963a..0696006 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/lib/main.dart b/lib/main.dart index dc39272..a7aa7d1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,7 +14,12 @@ class SpeedometerApp extends StatelessWidget { @override Widget build(BuildContext context) { - SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); return MultiProvider( providers: [ diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index d5bbe80..66cc84b 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -enum SpeedUnit { kmh, mph } +enum SpeedUnit { kmh, mph, knots } enum SpeedometerStyle { digital, analog } class SettingsProvider extends ChangeNotifier { @@ -21,7 +21,16 @@ class SettingsProvider extends ChangeNotifier { bool get showTripTime => _showTripTime; bool get showAccuracy => _showAccuracy; - String get speedUnitString => _speedUnit == SpeedUnit.kmh ? 'km/h' : 'mph'; + String get speedUnitString { + switch (_speedUnit) { + case SpeedUnit.kmh: + return 'km/h'; + case SpeedUnit.mph: + return 'mph'; + case SpeedUnit.knots: + return 'knots'; + } + } SettingsProvider() { _loadSettings(); @@ -89,10 +98,13 @@ class SettingsProvider extends ChangeNotifier { } double convertSpeed(double speedMps) { - if (_speedUnit == SpeedUnit.kmh) { - return speedMps * 3.6; - } else { - return speedMps * 2.237; + switch (_speedUnit) { + case SpeedUnit.kmh: + return speedMps * 3.6; + case SpeedUnit.mph: + return speedMps * 2.237; + case SpeedUnit.knots: + return speedMps * 1.944; } } } \ No newline at end of file diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index c42c66f..41964df 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -128,6 +128,17 @@ class SettingsScreen extends StatelessWidget { } }, ), + RadioListTile( + title: const Text('Knots'), + value: SpeedUnit.knots, + groupValue: settings.speedUnit, + onChanged: (value) { + if (value != null) { + settings.setSpeedUnit(value); + Navigator.pop(context); + } + }, + ), ], ), actions: [ @@ -183,4 +194,5 @@ class SettingsScreen extends StatelessWidget { ), ); } + } \ No newline at end of file diff --git a/lib/screens/speedometer_screen.dart b/lib/screens/speedometer_screen.dart index 525b263..9b9975e 100644 --- a/lib/screens/speedometer_screen.dart +++ b/lib/screens/speedometer_screen.dart @@ -5,6 +5,7 @@ import '../providers/settings_provider.dart'; import '../widgets/digital_speedometer.dart'; import '../widgets/analog_speedometer.dart'; import '../widgets/info_panel.dart'; +import '../widgets/speed_unit_selector.dart'; import 'settings_screen.dart'; class SpeedometerScreen extends StatefulWidget { @@ -62,7 +63,17 @@ class _SpeedometerScreenState extends State { children: [ Expanded( flex: 3, - child: _buildSpeedometer(locationProvider, settingsProvider), + child: Column( + children: [ + Expanded( + child: _buildSpeedometer(locationProvider, settingsProvider), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 8.0), + child: const SpeedUnitSelector(), + ), + ], + ), ), Expanded( flex: 1, @@ -81,17 +92,23 @@ class _SpeedometerScreenState extends State { Widget _buildSpeedometer(LocationProvider locationProvider, SettingsProvider settingsProvider) { final speed = settingsProvider.convertSpeed(locationProvider.currentSpeed); - if (settingsProvider.speedometerStyle == SpeedometerStyle.digital) { - return DigitalSpeedometer( - speed: speed, - unit: settingsProvider.speedUnitString, - ); - } else { - return AnalogSpeedometer( - speed: speed, - unit: settingsProvider.speedUnitString, - ); - } + return GestureDetector( + onTap: () { + final newStyle = settingsProvider.speedometerStyle == SpeedometerStyle.digital + ? SpeedometerStyle.analog + : SpeedometerStyle.digital; + settingsProvider.setSpeedometerStyle(newStyle); + }, + child: settingsProvider.speedometerStyle == SpeedometerStyle.digital + ? DigitalSpeedometer( + speed: speed, + unit: settingsProvider.speedUnitString, + ) + : AnalogSpeedometer( + speed: speed, + unit: settingsProvider.speedUnitString, + ), + ); } Widget _buildPermissionError(String message) { diff --git a/lib/widgets/speed_unit_selector.dart b/lib/widgets/speed_unit_selector.dart new file mode 100644 index 0000000..0113e0a --- /dev/null +++ b/lib/widgets/speed_unit_selector.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../providers/settings_provider.dart'; + +class SpeedUnitSelector extends StatelessWidget { + const SpeedUnitSelector({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, settings, child) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: SpeedUnit.values.map((unit) { + final isSelected = settings.speedUnit == unit; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: ElevatedButton( + onPressed: () => settings.setSpeedUnit(unit), + style: ElevatedButton.styleFrom( + backgroundColor: isSelected + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surface, + foregroundColor: isSelected + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSurface, + elevation: isSelected ? 4 : 1, + padding: const EdgeInsets.symmetric(vertical: 8), + ), + child: Text( + _getUnitDisplayText(unit), + style: TextStyle( + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + fontSize: 12, + ), + ), + ), + ), + ); + }).toList(), + ); + }, + ); + } + + String _getUnitDisplayText(SpeedUnit unit) { + switch (unit) { + case SpeedUnit.kmh: + return 'km/h'; + case SpeedUnit.mph: + return 'mph'; + case SpeedUnit.knots: + return 'knots'; + } + } +} \ No newline at end of file