import 'package:flutter/material.dart'; /// Visual theme token set for the AR-Autopilot display app. /// /// Every widget reads design tokens from [AutopilotTheme] via /// [AutopilotThemeProvider]. No widget hard-codes colors. /// /// ## Design invariants (apply to ALL themes) /// 1. **DISENGAGE** is always the highest-contrast element on screen. /// If the palette makes red ambiguous, it changes to amber (wine theme). /// 2. **Action buttons** (DODGE, ±1°, ±10°) must be legible at rest — /// operators work with gloves in rain; hover/tap is not reliable. /// 3. **Compass heading text** uses [textMain] with [accentGlowColor] glow. /// 4. **Set-point** always uses [setLight] (amber/gold) — cognitively stable /// across palettes; represents "operator intent". /// 5. **North mark** is always a warm color ([northColor]) — nautical convention. /// 6. **Touch targets**: 48×48 px nominal, 60×60 px for critical controls. /// 7. **No glow in light mode**: [accentGlowRadius] == 0.0 for `light` theme. class AutopilotTheme { const AutopilotTheme({ required this.id, required this.displayName, // Backgrounds required this.background, required this.backgroundMid, required this.backgroundDeep, required this.backgroundDeepest, this.backgroundGradient, // Panels required this.panelBackground, required this.panelBorder, // Text required this.textMain, required this.textMuted, required this.textSoft, required this.textDisabled, // Accent — heading arc, active mode indicator, rudder indicator required this.accentLight, required this.accentMid, required this.accentDark, required this.accentGlowRadius, required this.accentGlowColor, // Set-point / desired heading required this.setLight, required this.setDark, required this.setGlow, // Semantic states required this.okColor, required this.warnColor, required this.northColor, // DISENGAGE — always high contrast, always unambiguous required this.disengageBackground, required this.disengageText, required this.disengageBorder, required this.disengageGlow, // Action buttons (DODGE, ±1°, ±10°) required this.actionButtonBackground, required this.actionButtonBorder, required this.actionButtonText, required this.actionButtonGlow, }); final String id; final String displayName; // ── Backgrounds ──────────────────────────────────────────────────────────── /// Dominant background color. Used as solid fill (light) or radial /// gradient center (dark themes). See [backgroundDecoration]. final Color background; final Color backgroundMid; final Color backgroundDeep; final Color backgroundDeepest; /// Radial gradient for dark themes; `null` for the light theme /// (which uses a solid [background]). final Gradient? backgroundGradient; // ── Panels ────────────────────────────────────────────────────────────────── final Gradient panelBackground; final Color panelBorder; // ── Text ──────────────────────────────────────────────────────────────────── final Color textMain; final Color textMuted; final Color textSoft; final Color textDisabled; // ── Accent ────────────────────────────────────────────────────────────────── final Color accentLight; final Color accentMid; final Color accentDark; /// Blur radius for accent glow effect. Zero in light mode (no glow). final double accentGlowRadius; final Color accentGlowColor; // ── Set-point ─────────────────────────────────────────────────────────────── final Color setLight; final Color setDark; final Color setGlow; // ── Semantic states ───────────────────────────────────────────────────────── /// GPS OK, NMEA sentence valid, SOG within range. final Color okColor; /// Heading error, cross-track error warning. final Color warnColor; /// North mark on the compass rose. Always a warm color (never blue). final Color northColor; // ── DISENGAGE ─────────────────────────────────────────────────────────────── final Gradient disengageBackground; final Color disengageText; final Color disengageBorder; final Color disengageGlow; // ── Action buttons (DODGE, ±1°, ±10°) ────────────────────────────────────── final Gradient actionButtonBackground; final Color actionButtonBorder; final Color actionButtonText; final Color actionButtonGlow; // ── Computed helpers ──────────────────────────────────────────────────────── /// [BoxDecoration] for the full-screen background container. BoxDecoration get backgroundDecoration => backgroundGradient != null ? BoxDecoration(gradient: backgroundGradient) : BoxDecoration(color: background); /// [BoxShadow] list for accented glow (empty in light mode). List glowShadow(Color color, double radius) => radius > 0 ? [BoxShadow(color: color, blurRadius: radius, spreadRadius: 1)] : const []; }