Implement v1.1 enhancements: auto-rotate, interactive gauge, and extended units

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 <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2025-08-21 01:58:46 +09:00
parent b0125dff6b
commit 71b7611a48
9 changed files with 155 additions and 32 deletions
+6 -1
View File
@@ -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: [
+18 -6
View File
@@ -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;
}
}
}
+12
View File
@@ -128,6 +128,17 @@ class SettingsScreen extends StatelessWidget {
}
},
),
RadioListTile<SpeedUnit>(
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 {
),
);
}
}
+29 -12
View File
@@ -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<SpeedometerScreen> {
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<SpeedometerScreen> {
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) {
+57
View File
@@ -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<SettingsProvider>(
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';
}
}
}