Files
mikkeli 3aeb09d61f
/ crosscheck (push) Successful in 0s
CI / Analyze & Test (push) Successful in 13s
CI / Debug APK (push) Successful in 22s
Palette: seven roles (accent, good, caution added; highlight renamed alarm); no hard-coded colours left
background, foreground, subdued, accent (emphasis without danger), and the state
gradient good → caution → alarm; surface and onFill derived. smart-serow's
four-key themes still load (highlight = alarm; the new roles fall back).
Material's scheme follows: buttons in accent instead of the alarm colour.
Link state, mock banner, signal tiles, raw log, checklist button and marks,
camera page all take palette roles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-14 13:55:52 +09:00

96 lines
2.5 KiB
Dart

// Ported from smart-serow (pi/ui/lib/widgets/gps_compass.dart), itself from android-stuff/gps-speedo:
// a heading arrow and its bearing. Changes: the palette import and lint fixes.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../theme/palette.dart';
class GpsCompass extends StatelessWidget {
final double? heading;
final String? gpsState; // "acquiring", "fix", "lost"
const GpsCompass({super.key, this.heading, this.gpsState});
bool get _hasSignal => heading != null;
bool get _isAcquiring => gpsState == 'acquiring';
String get _displayHeading {
if (!_hasSignal) return 'N/A';
return '${(heading! % 360).round()}';
}
String get _compassDirection {
if (!_hasSignal) return '';
final directions = [
'N',
'NNE',
'NE',
'ENE',
'E',
'ESE',
'SE',
'SSE',
'S',
'SSW',
'SW',
'WSW',
'W',
'WNW',
'NW',
'NNW',
];
final index = ((heading! % 360) / 22.5).round() % 16;
return directions[index];
}
@override
Widget build(BuildContext context) {
final theme = AppTheme.of(context);
// No signal = subdued color, valid = foreground
// Heading: foreground; acquiring: caution; no receiver at all: alarm.
final iconColour = _hasSignal ? theme.foreground : (_isAcquiring ? theme.caution : theme.alarm);
// 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: _hasSignal ? angle : 0,
child: FittedBox(
fit: BoxFit.contain,
child: Icon(
_hasSignal ? Icons.navigation : Icons.navigation_outlined,
size: 120,
color: iconColour,
),
),
),
),
Flexible(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
_hasSignal
? "$_displayHeading $_compassDirection"
: (_isAcquiring ? "ACQ" : "N/A"),
style: TextStyle(
fontSize: 80,
color: theme.subdued, // less emphasis on text, let the icon have semantic colour
fontFamily: hudFont,
),
),
),
),
],
);
}
}