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:
+6
-1
@@ -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: [
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user