Palette: seven roles (accent, good, caution added; highlight renamed alarm); no hard-coded colours left
/ crosscheck (push) Successful in 0s
CI / Analyze & Test (push) Successful in 13s
CI / Debug APK (push) Successful in 22s

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
This commit is contained in:
2026-10-14 13:55:52 +09:00
parent 54ea278ca2
commit 3aeb09d61f
11 changed files with 165 additions and 72 deletions
+30 -17
View File
@@ -169,14 +169,15 @@ class MockBanner extends StatelessWidget {
@override
Widget build(BuildContext context) {
final p = AppTheme.of(context);
return Container(
color: Colors.amber.shade700,
color: p.caution,
padding: const EdgeInsets.symmetric(vertical: 6),
child: const Text(
child: Text(
'MOCK DATA — NOT FROM THE CAR',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
color: p.onFill(p.caution),
fontWeight: FontWeight.w900,
fontSize: 20,
letterSpacing: 2,
@@ -200,10 +201,11 @@ class LinkBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final b = boundary;
final p = AppTheme.of(context);
final (color, label) = switch (b.state) {
LinkState.up => (Colors.green, 'CONNECTED'),
LinkState.connecting => (Colors.orange, 'CONNECTING'),
LinkState.down => (Colors.red, 'DOWN'),
LinkState.up => (p.good, 'CONNECTED'),
LinkState.connecting => (p.caution, 'CONNECTING'),
LinkState.down => (p.alarm, 'DOWN'),
};
// Fixed-width numbers, padded with figure spaces (U+2007, digit-wide): the bar must
// not reflow every time a value gains a digit.
@@ -230,7 +232,10 @@ class LinkBar extends StatelessWidget {
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Chip(
label: Text(label),
label: Text(
label,
style: TextStyle(color: color, fontWeight: FontWeight.w600),
),
backgroundColor: color.withValues(alpha: 0.3),
),
Text(b.url, style: const TextStyle(fontFamily: 'monospace')),
@@ -317,7 +322,10 @@ Future<void> orinPowerOptions(BuildContext context, Boundary b) async {
label: const Text('Reboot'),
),
FilledButton.icon(
style: FilledButton.styleFrom(backgroundColor: Colors.red.shade700),
style: FilledButton.styleFrom(
backgroundColor: AppTheme.of(context).alarm,
foregroundColor: AppTheme.of(context).onFill(AppTheme.of(context).alarm),
),
onPressed: () => Navigator.of(context).pop('poweroff'),
icon: const Icon(Icons.power_settings_new),
label: const Text('Power off'),
@@ -403,26 +411,27 @@ class SignalTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
final p = AppTheme.of(context);
if (missing) {
return ListTile(
title: Text(
name,
style: const TextStyle(fontFamily: 'monospace', color: Colors.grey),
style: TextStyle(fontFamily: 'monospace', color: p.subdued),
),
subtitle: const Text(
subtitle: Text(
'MISSING · not in the latest state',
style: TextStyle(color: Colors.redAccent),
style: TextStyle(color: p.alarm),
),
trailing: Text.rich(
TextSpan(
children: [
const TextSpan(
TextSpan(
text: '?',
style: TextStyle(fontSize: 34, color: Colors.grey),
style: TextStyle(fontSize: 34, color: p.subdued),
),
TextSpan(
text: ' ${envelope['unit'] ?? ''}',
style: const TextStyle(fontSize: 16, color: Colors.grey),
style: TextStyle(fontSize: 16, color: p.subdued),
),
],
),
@@ -439,12 +448,13 @@ class SignalTile extends StatelessWidget {
fontSize: 34,
fontWeight: FontWeight.w600,
fontFeatures: const [FontFeature.tabularFigures()],
color: stale || v == null ? Colors.grey : null,
color: stale || v == null ? p.subdued : p.foreground,
fontStyle: stale ? FontStyle.italic : null,
);
return ListTile(
title: Text(name, style: const TextStyle(fontFamily: 'monospace')),
subtitle: Text(
style: stale ? TextStyle(color: p.caution) : null,
[
if (stale) quality.toUpperCase(),
if (v == null) 'no value',
@@ -479,11 +489,14 @@ class RawLog extends StatelessWidget {
if (b.pending.isNotEmpty || b.acks.isNotEmpty) ...[
Text('Commands', style: Theme.of(context).textTheme.titleSmall),
for (final p in b.pending)
Text('… ${p.cmd} ${p.args['text'] ?? ''} (unacked)'),
Text(
'… ${p.cmd} ${p.args['text'] ?? ''} (unacked)',
style: TextStyle(color: AppTheme.of(context).caution),
),
for (final a in b.acks)
Text(
'${a.ok ? '✓' : '✗'} ${a.detail}',
style: TextStyle(color: a.ok ? null : Colors.redAccent),
style: TextStyle(color: a.ok ? AppTheme.of(context).good : AppTheme.of(context).alarm),
),
const Divider(),
],