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