diff --git a/pi/ui/lib/screens/dashboard_screen.dart b/pi/ui/lib/screens/dashboard_screen.dart index 55fac30..d7b7cad 100644 --- a/pi/ui/lib/screens/dashboard_screen.dart +++ b/pi/ui/lib/screens/dashboard_screen.dart @@ -203,11 +203,11 @@ class _DashboardScreenState extends State { wsState: _wsState, ), - const SizedBox(height: 5), + const SizedBox(height: 2), // Main content area - big stat boxes Expanded( - flex: 8, + flex: 7, child: Row( children: [ // Attitude indicator (whiskey mark) @@ -231,11 +231,12 @@ class _DashboardScreenState extends State { // Bottom stats row Expanded( - flex: 2, + flex: 3, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ StatBox(value: _formatInt(_rpm), label: 'RPM', isWarning: () => (_rpm ?? 0) > 4000), + GpsCompass(heading: 147), StatBox(value: _formatGear(_gear), label: 'GEAR'), ], ), diff --git a/pi/ui/lib/widgets/gps_compass.dart b/pi/ui/lib/widgets/gps_compass.dart new file mode 100644 index 0000000..64ad128 --- /dev/null +++ b/pi/ui/lib/widgets/gps_compass.dart @@ -0,0 +1,61 @@ +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import '../theme/app_theme.dart'; + +class GpsCompass extends StatelessWidget { + final double? heading; + + const GpsCompass({super.key, this.heading}); + + bool get _hasSignal => heading != null && !heading!.isNaN && heading! >= 0 && heading! < 360; + + String get _displayHeading { + if (!_hasSignal) return '—°'; + return '${heading!.round()}°'; + } + + @override + Widget build(BuildContext context) { + final theme = AppTheme.of(context); + + // No signal = subdued color, valid = foreground + final color = _hasSignal ? theme.foreground : theme.subdued; + + // Convert to radians, 0 = no rotation when no signal + final angle = _hasSignal ? (heading! * math.pi / 180.0) : 0.0; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 3, + child: Transform.rotate( + angle: angle, + child: FittedBox( + fit: BoxFit.contain, + child: Icon( + Icons.navigation, + size: 60, + color: color, + ), + ), + ), + ), + Flexible( + flex: 1, + child: FittedBox( + fit: BoxFit.contain, + child: Text( + _displayHeading, + style: TextStyle( + fontSize: 30, + color: color, + fontFamily: 'DIN1451', + ), + ), + ), + ), + ], + ); + } +}