Dash page and smart-serow's palette, boxed widgets, attitude and G-meter
/ crosscheck (push) Successful in 1s
CI / Analyze & Test (push) Successful in 9s
CI / Debug APK (push) Successful in 17s

Ported from smart-serow's HUD: the four-colour palette with dark/bright variants
(same theme JSON format, loaded at runtime; YUKART_THEME picks one), StatBox and
StatBoxMain, WhiskeyMark and AccelGraph. The whole app takes its colours from the
palette. Dash: attitude, G-meter, RPM, GNSS speed, pedal, coolant and telltales,
each from its contracts signal; missing and stale draw in subdued.

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-13 23:25:28 +09:00
parent eae93b0697
commit b083cf31dd
9 changed files with 1270 additions and 13 deletions
+30 -13
View File
@@ -8,8 +8,10 @@ import 'package:flutter/material.dart';
import 'boundary.dart';
import 'camview.dart';
import 'checklist.dart';
import 'dash.dart';
import 'linktest.dart';
import 'net.dart';
import 'theme/palette.dart';
/// Override for development, e.g. `--dart-define=YUKART_GW=ws://halogen.local:7700/`.
const defaultUrl = String.fromEnvironment(
@@ -19,6 +21,7 @@ const defaultUrl = String.fromEnvironment(
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
themes = await Themes.load();
final usb = UsbLink();
await usb.bindFor(Uri.parse(defaultUrl));
final boundary = Boundary(defaultUrl)..start();
@@ -58,6 +61,9 @@ Future<void> main() async {
final navigatorKey = GlobalKey<NavigatorState>();
/// The palette (lib/theme/palette.dart), loaded before the first frame.
late final Themes themes;
Widget linkTestPage(Boundary b, LinkTest t, UsbLink usb) => LinkTestPage(
test: t,
host: Uri.parse(b.url).host,
@@ -77,19 +83,21 @@ class YukartApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Yukart',
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.teal,
brightness: Brightness.dark,
useMaterial3: true,
),
home: ListenableBuilder(
listenable: Listenable.merge([boundary, usb]),
builder: (context, _) =>
Readout(boundary: boundary, usb: usb, linkTest: linkTest),
// Every page takes its colours from the palette; switching dark/bright rebuilds them all.
return ListenableBuilder(
listenable: themes,
builder: (context, _) => MaterialApp(
title: 'Yukart',
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
theme: themes.material(),
builder: (context, child) =>
AppTheme(palette: themes.current, child: child!),
home: ListenableBuilder(
listenable: Listenable.merge([boundary, usb]),
builder: (context, _) =>
Readout(boundary: boundary, usb: usb, linkTest: linkTest),
),
),
);
}
@@ -233,6 +241,15 @@ class LinkBar extends StatelessWidget {
Text(
'USB link ${usb.up ? 'up' : 'absent'}${usb.bound ? ', bound' : ''}',
),
TextButton.icon(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => DashPage(boundary: b, themes: themes),
),
),
icon: const Icon(Icons.dashboard),
label: const Text('Dash'),
),
TextButton.icon(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(