Files
PilotVault/Fly Agent App/lib/theme.dart
T
tajniak81andClaude Opus 5.5 3dd31b257a Add the Fly Agent App, a Remote ID companion to DJI Fly
A Flutter app that runs next to DJI Fly without the DJI SDK. It receives the
aircraft's Remote ID (Bluetooth, BT5, Wi-Fi NaN and Beacon) in a foreground
service, streams the paired aircraft to the hub over /ws/device, and turns
take-off to landing into a logbook entry filed through /api/flights, queued
when offline. Covers phases A0-A3 of the Fly Agent plan; needs no server change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:25:52 +02:00

116 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
/// PilotVault mobile tokens — the same values as the Fly App's `PV` (Signal
/// Blue over Vault Navy, Space Grotesk for chrome, Space Mono for data).
class PV {
PV._();
static const String fontSans = 'Space Grotesk';
static const String fontMono = 'Space Mono';
static const Color navy = Color(0xFF0F1E3D);
static const Color accent = Color(0xFF3D7BF0);
static const Color ready = Color(0xFF1F8A5B);
static const Color caution = Color(0xFFD9852B);
static const Color warning = Color(0xFFD64545);
static const Color surface0 = Color(0xFFEEF0F3);
static const Color surface1 = Color(0xFFFFFFFF);
static const Color surface2 = Color(0xFFF6F7F9);
static const Color ink = navy;
static const Color inkSecondary = Color(0xFF5A6B85);
static const Color inkMuted = Color(0xFF97A1B0);
static const Color line = Color(0xFFDCE0E7);
static const double radius = 14;
static const TextStyle telemetry = TextStyle(
fontFamily: fontMono,
fontSize: 26,
fontWeight: FontWeight.w700,
height: 1.0,
color: ink,
fontFeatures: <FontFeature>[FontFeature.tabularFigures()],
);
static const TextStyle label = TextStyle(
fontFamily: fontMono,
fontSize: 11,
fontWeight: FontWeight.w700,
letterSpacing: 1.5,
color: inkMuted,
);
static const TextStyle mono = TextStyle(
fontFamily: fontMono,
fontSize: 13,
fontWeight: FontWeight.w700,
color: ink,
fontFeatures: <FontFeature>[FontFeature.tabularFigures()],
);
static const TextStyle caption = TextStyle(fontFamily: fontSans, fontSize: 12, color: inkSecondary);
static ThemeData theme() {
final ColorScheme scheme = ColorScheme.fromSeed(
seedColor: accent,
primary: accent,
surface: surface1,
error: warning,
);
return ThemeData(
colorScheme: scheme,
fontFamily: fontSans,
scaffoldBackgroundColor: surface0,
appBarTheme: const AppBarTheme(
backgroundColor: surface1,
foregroundColor: ink,
elevation: 0,
scrolledUnderElevation: 0.5,
titleTextStyle: TextStyle(fontFamily: fontSans, fontSize: 18, fontWeight: FontWeight.w600, color: ink),
),
cardTheme: CardThemeData(
color: surface1,
elevation: 0,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
side: const BorderSide(color: line),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: surface2,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: line)),
enabledBorder:
OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: line)),
),
navigationBarTheme: const NavigationBarThemeData(backgroundColor: surface1, indicatorColor: Color(0x263D7BF0)),
);
}
}
/// Mono ALL-CAPS section label.
class SectionLabel extends StatelessWidget {
const SectionLabel(this.text, {super.key});
final String text;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.fromLTRB(4, 16, 4, 8),
child: Text(text.toUpperCase(), style: PV.label),
);
}
class StatusDot extends StatelessWidget {
const StatusDot(this.color, {super.key, this.size = 8});
final Color color;
final double size;
@override
Widget build(BuildContext context) => Container(
width: size,
height: size,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
);
}