auris 0.2.0 copy "auris: ^0.2.0" to clipboard
auris: ^0.2.0 copied to clipboard

A Flutter UI kit and Material 3 theme with a warm amber-on-near-black, chamfered sci-fi aesthetic — a drop-in ThemeData plus a library of custom HUD widgets.

example/lib/main.dart

import 'dart:async';

import 'package:auris/auris.dart';
import 'package:auris/auris_widgets.dart';
import 'package:flutter/material.dart';

void main() => runApp(const AurisExampleApp());

/// A non-default accent option for the customization control. The example app
/// is not the package, so local `Color` literals are allowed here (the
/// no-raw-literals rule applies to the library, not to consumers).
class _AccentOption {
  const _AccentOption(this.label, this.color);

  /// `null` color means "the canonical default" — no accent override.
  final String label;
  final Color? color;
}

/// The accent palette the showcase can flip between: the default amber/gold
/// ramp plus a cool teal, a magenta, and a green, so flipping the control
/// re-skins every themed Material widget AND every Auris custom widget at once.
const List<_AccentOption> _accentOptions = <_AccentOption>[
  // null = no override → the kit's amber accent: bright gold on dark, the same
  // amber darkened to a bronze on light (same hue, adjusted lightness).
  _AccentOption('AMBER', null),
  _AccentOption('TEAL', Color(0xFF35E0C0)),
  _AccentOption('MAGENTA', Color(0xFFE048B0)),
  _AccentOption('GREEN', Color(0xFF6AD050)),
];

/// Preset steps for the bevel (corner-cut) and glow (depth-intensity) overrides,
/// labelled TIGHT / NORMAL / BOLD so their effect is legible at a glance.
class _ScaleStep {
  const _ScaleStep(this.label, this.value);

  final String label;
  final double value;
}

const List<_ScaleStep> _scaleSteps = <_ScaleStep>[
  _ScaleStep('TIGHT', 0.5),
  _ScaleStep('NORMAL', 1.0),
  _ScaleStep('BOLD', 2.0),
];

/// The showcase: a scrollable app that applies [AurisTheme.light] and renders
/// the re-skinned Material widgets — buttons (incl. segmented and toggle
/// buttons), inputs, selection controls and sliders; surfaces and overlays
/// (cards, dialog, snackbar, material banner, bottom sheet, tooltip, popup menu,
/// date and time pickers); menus (menu anchor / menu bar); navigation chrome
/// (tab bar, navigation bar, bottom navigation bar, bottom app bar, navigation
/// drawer); and data / feedback widgets (data table, scrollbar, list / expansion
/// tile, progress, badge, carousel, stepper) — each section introduced by a
/// monospace uppercase amber header (§spec:showcase). Every newly themed
/// component is rendered so a reviewer can confirm none falls back to default
/// Material (§road:showcase-missing-components).
///
/// It is stateful so the customization control near the top can lift the live
/// accent / bevel / glow overrides up to here; changing any of them rebuilds the
/// `MaterialApp` theme via [AurisTheme.light], and the whole showcase below
/// recolors and re-shapes because every widget reads the resolved scheme — that
/// propagation is the proof (§spec:customization, §road:customization-showcase).
class AurisExampleApp extends StatefulWidget {
  const AurisExampleApp({super.key});

  @override
  State<AurisExampleApp> createState() => _AurisExampleAppState();
}

class _AurisExampleAppState extends State<AurisExampleApp> {
  Color? _accent = _accentOptions.first.color;
  double _bevelScale = 1.0;
  double _glowScale = 1.0;
  Brightness _brightness = Brightness.dark;

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = _brightness == Brightness.light
        ? AurisTheme.light(
            accent: _accent,
            bevelScale: _bevelScale,
            glowScale: _glowScale,
          )
        : AurisTheme.dark(
            accent: _accent,
            bevelScale: _bevelScale,
            glowScale: _glowScale,
          );
    return MaterialApp(
      title: 'Auris — Core Controls',
      debugShowCheckedModeBanner: false,
      theme: theme,
      home: _ShowcaseScreen(
        accent: _accent,
        bevelScale: _bevelScale,
        glowScale: _glowScale,
        brightness: _brightness,
        onAccentChanged: (Color? c) => setState(() => _accent = c),
        onBevelChanged: (double v) => setState(() => _bevelScale = v),
        onGlowChanged: (double v) => setState(() => _glowScale = v),
        onBrightnessChanged: (Brightness b) => setState(() => _brightness = b),
      ),
    );
  }
}

class _ShowcaseScreen extends StatefulWidget {
  const _ShowcaseScreen({
    required this.accent,
    required this.bevelScale,
    required this.glowScale,
    required this.brightness,
    required this.onAccentChanged,
    required this.onBevelChanged,
    required this.onGlowChanged,
    required this.onBrightnessChanged,
  });

  final Color? accent;
  final double bevelScale;
  final double glowScale;
  final Brightness brightness;
  final ValueChanged<Color?> onAccentChanged;
  final ValueChanged<double> onBevelChanged;
  final ValueChanged<double> onGlowChanged;
  final ValueChanged<Brightness> onBrightnessChanged;

  @override
  State<_ShowcaseScreen> createState() => _ShowcaseScreenState();
}

class _ShowcaseScreenState extends State<_ShowcaseScreen> {
  bool _checkbox = true;
  bool _checkboxOff = false;
  int _radio = 0;
  bool _switch = true;
  bool _switchOff = false;
  bool _aurisSwitch = true;
  bool _aurisSwitchAlt = false;
  double _progress = 0.45;
  String? _aurisSelect = 'beta';
  double _slider = 0.4;
  double _sliderStepped = 30;
  final Set<String> _chips = <String>{'CORE'};
  String _segment = 'AUTO';
  String? _dropdown = 'ALPHA';
  int _navIndex = 0;
  int _bottomNavIndex = 0;
  int _drawerIndex = 0;
  final Set<int> _toggle = <int>{1};
  int _step = 1;
  bool _showError = true;
  DateTime? _date;
  TimeOfDay? _time;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final ScrollController _scrollbarController = ScrollController();

  // Width of each CarouselView item; reused as the OverflowBox max width so the
  // trailing partial item lays out at full width and clips cleanly.
  static const double _carouselItemExtent = 160;

  // Live-appending terminal: a Timer pushes a new log line periodically.
  final List<AurisTerminalLine> _log = <AurisTerminalLine>[
    const AurisTerminalLine(
      '> boot sequence initiated',
      type: AurisTerminalLineType.augment,
    ),
    const AurisTerminalLine(
      '  loading core modules ... ok',
      type: AurisTerminalLineType.ok,
    ),
  ];
  Timer? _logTimer;
  int _logTick = 0;

  static const List<AurisTerminalLine> _logSamples = <AurisTerminalLine>[
    AurisTerminalLine(
      '  diagnostic pass complete',
      type: AurisTerminalLineType.ok,
    ),
    AurisTerminalLine('  telemetry uplink nominal'),
    AurisTerminalLine(
      '! thermal margin low',
      type: AurisTerminalLineType.warning,
    ),
    AurisTerminalLine(
      '  augment graft synced',
      type: AurisTerminalLineType.augment,
    ),
    AurisTerminalLine(
      'x packet checksum mismatch',
      type: AurisTerminalLineType.error,
    ),
  ];

  @override
  void initState() {
    super.initState();
    _logTimer = Timer.periodic(const Duration(seconds: 2), (Timer _) {
      setState(() {
        final AurisTerminalLine sample =
            _logSamples[_logTick % _logSamples.length];
        _log.add(
          AurisTerminalLine(
            '${sample.text} [${_logTick.toString().padLeft(3, '0')}]',
            type: sample.type,
          ),
        );
        _logTick++;
        // Bound the buffer so the demo does not grow without limit.
        if (_log.length > 40) {
          _log.removeRange(0, _log.length - 40);
        }
      });
    });
  }

  @override
  void dispose() {
    _logTimer?.cancel();
    _scrollbarController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    final TextTheme text = theme.textTheme;

    // Read the resolved scheme from the ThemeExtension to prove it resolves,
    // and drive the section headers and AppBar from its role colors.
    final AurisScheme scheme = theme.extension<AurisScheme>()!;

    // The showcase doubles as the hosted live demo, which must stay legible on
    // a phone browser as well as a desktop one (§spec:live-demo,
    // §road:example-responsive-layout). The single column already reflows —
    // it caps at maxWidth and centres on wide viewports — so the only phone
    // concession needed is tighter side padding so narrow screens keep usable
    // content width.
    final double viewportWidth = MediaQuery.sizeOf(context).width;
    final double horizontalPad = viewportWidth < 400 ? 16 : 24;

    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text('AURIS // CORE CONTROLS', style: text.titleLarge),
        backgroundColor: scheme.surfacePanel,
      ),
      // A themed NavigationDrawer — chamfered gold indicator, mono labels.
      drawer: NavigationDrawer(
        selectedIndex: _drawerIndex,
        onDestinationSelected: (int i) {
          setState(() => _drawerIndex = i);
          _scaffoldKey.currentState?.closeDrawer();
        },
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.fromLTRB(28, 24, 16, 12),
            child: Text('NAVIGATION', style: text.titleMedium),
          ),
          const NavigationDrawerDestination(
            icon: Icon(Icons.dashboard_outlined),
            selectedIcon: Icon(Icons.dashboard),
            label: Text('HUD'),
          ),
          const NavigationDrawerDestination(
            icon: Icon(Icons.sensors_outlined),
            selectedIcon: Icon(Icons.sensors),
            label: Text('SENSORS'),
          ),
          const NavigationDrawerDestination(
            icon: Icon(Icons.settings_outlined),
            selectedIcon: Icon(Icons.settings),
            label: Text('CONFIG'),
          ),
        ],
      ),
      body: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 560),
          child: SingleChildScrollView(
            padding: EdgeInsets.symmetric(
              horizontal: horizontalPad,
              vertical: 24,
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Text('SYSTEM ONLINE', style: text.displaySmall),
                const SizedBox(height: 8),
                Text(
                  'Augmentation-era interface. Amber-on-near-black, chamfered '
                  'geometry, glow in place of drop shadow.',
                  style: text.bodyLarge,
                ),
                const SizedBox(height: 24),

                // ---- CUSTOMIZATION ------------------------------------------
                // Lifts the accent / bevel / glow overrides into the root theme
                // controller. Flipping any of these rebuilds AurisTheme.light,
                // and every section below recolors / re-shapes because it reads
                // the resolved scheme — no per-widget edits required
                // (§spec:customization, §road:customization-showcase).
                const _SectionHeader('CUSTOMIZATION'),
                _CustomizationControl(
                  accent: widget.accent,
                  bevelScale: widget.bevelScale,
                  glowScale: widget.glowScale,
                  brightness: widget.brightness,
                  onAccentChanged: widget.onAccentChanged,
                  onBevelChanged: widget.onBevelChanged,
                  onGlowChanged: widget.onGlowChanged,
                  onBrightnessChanged: widget.onBrightnessChanged,
                ),
                const SizedBox(height: 32),

                // ---- ACCESSIBILITY ------------------------------------------
                // Surfaces the two cross-cutting a11y behaviors for review:
                // the live reduced-motion state (driven by the OS setting via
                // MediaQuery.disableAnimations) and a prompt to tab through the
                // controls and watch the gold focus ring travel
                // (§spec:accessibility, §road:polish-showcase-verification).
                const _AccessibilityBanner(),
                const SizedBox(height: 32),

                // ---- BUTTONS -------------------------------------------------
                const _SectionHeader('BUTTONS'),
                Row(
                  children: <Widget>[
                    Expanded(
                      child: FilledButton(
                        onPressed: () {},
                        child: const Text('ENGAGE'),
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: ElevatedButton(
                        onPressed: () {},
                        child: const Text('STANDBY'),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 12),
                Row(
                  children: <Widget>[
                    Expanded(
                      child: OutlinedButton(
                        onPressed: () {},
                        child: const Text('DIAGNOSTIC'),
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: TextButton(
                        onPressed: () {},
                        child: const Text('DETAILS'),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 12),
                Row(
                  children: <Widget>[
                    IconButton(
                      onPressed: () {},
                      icon: const Icon(Icons.power_settings_new),
                    ),
                    const SizedBox(width: 8),
                    IconButton(onPressed: () {}, icon: const Icon(Icons.tune)),
                    const Spacer(),
                    FloatingActionButton(
                      onPressed: () {},
                      child: const Icon(Icons.add),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                SegmentedButton<String>(
                  segments: const <ButtonSegment<String>>[
                    ButtonSegment<String>(value: 'AUTO', label: Text('AUTO')),
                    ButtonSegment<String>(value: 'MANUAL', label: Text('MAN')),
                    ButtonSegment<String>(value: 'OFF', label: Text('OFF')),
                  ],
                  selected: <String>{_segment},
                  showSelectedIcon: false,
                  onSelectionChanged: (Set<String> s) =>
                      setState(() => _segment = s.first),
                ),
                const SizedBox(height: 12),
                // ToggleButtons — the legacy M2 segmented group, themed gold.
                ToggleButtons(
                  isSelected: <bool>[
                    _toggle.contains(0),
                    _toggle.contains(1),
                    _toggle.contains(2),
                  ],
                  onPressed: (int i) => setState(() {
                    if (_toggle.contains(i)) {
                      _toggle.remove(i);
                    } else {
                      _toggle.add(i);
                    }
                  }),
                  children: const <Widget>[
                    Padding(
                      padding: EdgeInsets.symmetric(horizontal: 16),
                      child: Text('LOW'),
                    ),
                    Padding(
                      padding: EdgeInsets.symmetric(horizontal: 16),
                      child: Text('MED'),
                    ),
                    Padding(
                      padding: EdgeInsets.symmetric(horizontal: 16),
                      child: Text('HIGH'),
                    ),
                  ],
                ),
                const SizedBox(height: 12),
                // Disabled variants — null onPressed dims to 50% with no hover.
                const Row(
                  children: <Widget>[
                    Expanded(
                      child: FilledButton(
                        onPressed: null,
                        child: Text('DISABLED'),
                      ),
                    ),
                    SizedBox(width: 12),
                    Expanded(
                      child: OutlinedButton(
                        onPressed: null,
                        child: Text('DISABLED'),
                      ),
                    ),
                    SizedBox(width: 12),
                    IconButton(onPressed: null, icon: Icon(Icons.block)),
                  ],
                ),

                // ---- INPUTS --------------------------------------------------
                const _SectionHeader('INPUTS'),
                const TextField(
                  decoration: InputDecoration(
                    labelText: 'CALLSIGN',
                    hintText: 'Enter identifier',
                  ),
                ),
                const SizedBox(height: 16),
                const TextField(
                  obscureText: true,
                  // A hexagon mask glyph ties the obscured field to the kit's
                  // hex-ornament motif instead of a generic bullet.
                  obscuringCharacter: '⬡',
                  decoration: InputDecoration(
                    labelText: 'ACCESS KEY',
                    suffixIcon: Icon(Icons.lock_outline),
                  ),
                ),
                const SizedBox(height: 16),
                const TextField(
                  decoration: InputDecoration(
                    labelText: 'TARGET VECTOR',
                    errorText: 'OUT OF RANGE',
                  ),
                ),
                const SizedBox(height: 16),
                Text('CHANNEL', style: text.labelMedium),
                const SizedBox(height: 6),
                AurisSelect<String>(
                  value: _dropdown,
                  onChanged: (String v) => setState(() => _dropdown = v),
                  options: const <AurisSelectOption<String>>[
                    AurisSelectOption<String>(value: 'ALPHA', label: 'ALPHA'),
                    AurisSelectOption<String>(value: 'BRAVO', label: 'BRAVO'),
                    AurisSelectOption<String>(
                      value: 'CHARLIE',
                      label: 'CHARLIE',
                    ),
                  ],
                ),

                // ---- SELECTION CONTROLS -------------------------------------
                const _SectionHeader('SELECTION CONTROLS'),
                Row(
                  children: <Widget>[
                    Checkbox(
                      value: _checkbox,
                      onChanged: (bool? v) =>
                          setState(() => _checkbox = v ?? false),
                    ),
                    Text('PRIMARY LINK', style: text.bodyMedium),
                    const SizedBox(width: 16),
                    Checkbox(
                      value: _checkboxOff,
                      onChanged: (bool? v) =>
                          setState(() => _checkboxOff = v ?? false),
                    ),
                    Text('BACKUP', style: text.bodyMedium),
                    const SizedBox(width: 16),
                    const Checkbox(value: true, onChanged: null),
                    Text('LOCKED', style: text.bodyMedium),
                  ],
                ),
                const SizedBox(height: 8),
                Wrap(
                  spacing: 20,
                  runSpacing: 8,
                  children: <Widget>[
                    AurisRadio<int>(
                      value: 0,
                      groupValue: _radio,
                      onChanged: (int v) => setState(() => _radio = v),
                      label: 'LOW',
                    ),
                    AurisRadio<int>(
                      value: 1,
                      groupValue: _radio,
                      onChanged: (int v) => setState(() => _radio = v),
                      label: 'HIGH',
                    ),
                    AurisRadio<int>(
                      value: 2,
                      groupValue: _radio,
                      onChanged: null,
                      label: 'DISABLED',
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                Wrap(
                  spacing: 20,
                  runSpacing: 8,
                  children: <Widget>[
                    AurisSwitch(
                      value: _switch,
                      onChanged: (bool v) => setState(() => _switch = v),
                      label: 'SHIELDS',
                    ),
                    AurisSwitch(
                      value: _switchOff,
                      onChanged: (bool v) => setState(() => _switchOff = v),
                      label: 'CLOAK',
                    ),
                    const AurisSwitch(
                      value: false,
                      onChanged: null,
                      label: 'AUX',
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: <Widget>[
                    for (final String label in <String>[
                      'CORE',
                      'SENSORS',
                      'COMMS',
                      'NAV',
                    ])
                      FilterChip(
                        label: Text(label),
                        selected: _chips.contains(label),
                        onSelected: (bool on) => setState(() {
                          if (on) {
                            _chips.add(label);
                          } else {
                            _chips.remove(label);
                          }
                        }),
                      ),
                    const Chip(label: Text('OFFLINE')),
                  ],
                ),

                // ---- SLIDERS -------------------------------------------------
                const _SectionHeader('SLIDERS'),
                Text('THROTTLE', style: text.labelMedium),
                Slider(
                  value: _slider,
                  onChanged: (double v) => setState(() => _slider = v),
                ),
                const SizedBox(height: 8),
                Text('POWER ALLOCATION', style: text.labelMedium),
                Slider(
                  value: _sliderStepped,
                  min: 0,
                  max: 100,
                  divisions: 10,
                  label: '${_sliderStepped.round()}%',
                  onChanged: (double v) => setState(() => _sliderStepped = v),
                ),
                const SizedBox(height: 8),
                Text('LOCKED (DISABLED)', style: text.labelMedium),
                const Slider(value: 0.6, onChanged: null),

                // ---- CARDS & PANELS -----------------------------------------
                const _SectionHeader('CARDS & PANELS'),
                Card(
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Text('REACTOR CORE', style: text.titleMedium),
                        const SizedBox(height: 8),
                        Text(
                          'Chamfered panel surface with a resting outline in '
                          'place of a Material drop shadow.',
                          style: text.bodyMedium,
                        ),
                        const SizedBox(height: 12),
                        Row(
                          children: <Widget>[
                            const Badge(label: Text('NOMINAL')),
                            const SizedBox(width: 12),
                            Tooltip(
                              message: 'STABLE',
                              child: Icon(
                                Icons.shield_outlined,
                                color: scheme.primaryActive,
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),

                // ---- OVERLAYS -----------------------------------------------
                const _SectionHeader('OVERLAYS'),
                Text(
                  'Trigger overlays — none are pre-opened.',
                  style: text.bodyMedium,
                ),
                const SizedBox(height: 12),
                Wrap(
                  spacing: 12,
                  runSpacing: 12,
                  children: <Widget>[
                    OutlinedButton(
                      onPressed: _showDialog,
                      child: const Text('DIALOG'),
                    ),
                    OutlinedButton(
                      onPressed: _showSnackBar,
                      child: const Text('SNACKBAR'),
                    ),
                    OutlinedButton(
                      onPressed: _showBottomSheet,
                      child: const Text('SHEET'),
                    ),
                    PopupMenuButton<String>(
                      tooltip: 'MENU',
                      icon: const Icon(Icons.more_vert),
                      itemBuilder: (BuildContext context) =>
                          const <PopupMenuEntry<String>>[
                            PopupMenuItem<String>(
                              value: 'ARM',
                              child: Text('ARM'),
                            ),
                            PopupMenuItem<String>(
                              value: 'DISARM',
                              child: Text('DISARM'),
                            ),
                            PopupMenuItem<String>(
                              value: 'RESET',
                              child: Text('RESET'),
                            ),
                          ],
                    ),
                    OutlinedButton(
                      onPressed: _showBanner,
                      child: const Text('BANNER'),
                    ),
                    OutlinedButton(
                      onPressed: _pickDate,
                      child: const Text('DATE'),
                    ),
                    OutlinedButton(
                      onPressed: _pickTime,
                      child: const Text('TIME'),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                // The chosen date / time, proving the picker round-trips a value.
                Text(
                  'SELECTED: '
                  '${_date == null ? '—' : '${_date!.year}-'
                            '${_date!.month.toString().padLeft(2, '0')}-'
                            '${_date!.day.toString().padLeft(2, '0')}'} '
                  '${_time == null ? '' : _time!.format(context)}',
                  style: text.labelMedium,
                ),

                // ---- MENUS --------------------------------------------------
                // MenuAnchor + MenuBar exercise the menu / menuBar / menuButton
                // themes (the MenuAnchor surfaces) — chamfered panel popups with
                // monospace rows.
                const _SectionHeader('MENUS'),
                Text('MENU ANCHOR', style: text.labelMedium),
                const SizedBox(height: 8),
                // Align the trigger to its content width — the surrounding
                // Column stretches children, but a menu button shouldn't span
                // full width or its small popup reads as detached / overlapping.
                Align(
                  alignment: Alignment.centerLeft,
                  child: MenuAnchor(
                    // Drop the popup 6px below the trigger so it doesn't sit
                    // flush against the button — matching AurisSelect's gap.
                    alignmentOffset: const Offset(0, 6),
                    builder:
                        (
                          BuildContext context,
                          MenuController controller,
                          Widget? child,
                        ) {
                          return OutlinedButton.icon(
                            onPressed: () => controller.isOpen
                                ? controller.close()
                                : controller.open(),
                            icon: const Icon(Icons.expand_more),
                            label: const Text('ACTIONS'),
                          );
                        },
                    menuChildren: <Widget>[
                      MenuItemButton(
                        leadingIcon: const Icon(Icons.play_arrow),
                        onPressed: () {},
                        child: const Text('ENGAGE'),
                      ),
                      MenuItemButton(
                        leadingIcon: const Icon(Icons.pause),
                        onPressed: () {},
                        child: const Text('HOLD'),
                      ),
                      MenuItemButton(
                        leadingIcon: const Icon(Icons.stop),
                        onPressed: () {},
                        child: const Text('ABORT'),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 16),
                Text('MENU BAR', style: text.labelMedium),
                const SizedBox(height: 8),
                SizedBox(
                  height: 48,
                  child: MenuBar(
                    children: <Widget>[
                      SubmenuButton(
                        menuChildren: <Widget>[
                          MenuItemButton(
                            onPressed: () {},
                            child: const Text('NEW SCAN'),
                          ),
                          MenuItemButton(
                            onPressed: () {},
                            child: const Text('CALIBRATE'),
                          ),
                        ],
                        child: const Text('SYSTEM'),
                      ),
                      SubmenuButton(
                        menuChildren: <Widget>[
                          MenuItemButton(
                            onPressed: () {},
                            child: const Text('TELEMETRY'),
                          ),
                          MenuItemButton(
                            onPressed: () {},
                            child: const Text('DIAGNOSTICS'),
                          ),
                        ],
                        child: const Text('VIEW'),
                      ),
                    ],
                  ),
                ),

                // ---- NAVIGATION ---------------------------------------------
                const _SectionHeader('NAVIGATION'),
                Text('TAB BAR', style: text.labelMedium),
                const SizedBox(height: 8),
                DefaultTabController(
                  length: 3,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: <Widget>[
                      const TabBar(
                        tabs: <Widget>[
                          Tab(text: 'STATUS'),
                          Tab(text: 'POWER'),
                          Tab(text: 'COMMS'),
                        ],
                      ),
                      SizedBox(
                        height: 64,
                        child: TabBarView(
                          children: <Widget>[
                            Center(
                              child: Text(
                                'ALL SYSTEMS NOMINAL',
                                style: text.bodyMedium,
                              ),
                            ),
                            Center(
                              child: Text(
                                'REACTOR AT 82%',
                                style: text.bodyMedium,
                              ),
                            ),
                            Center(
                              child: Text(
                                'UPLINK ESTABLISHED',
                                style: text.bodyMedium,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 16),
                Text('NAVIGATION BAR', style: text.labelMedium),
                const SizedBox(height: 8),
                NavigationBar(
                  selectedIndex: _navIndex,
                  onDestinationSelected: (int i) =>
                      setState(() => _navIndex = i),
                  destinations: const <NavigationDestination>[
                    NavigationDestination(
                      icon: Icon(Icons.dashboard_outlined),
                      selectedIcon: Icon(Icons.dashboard),
                      label: 'HUD',
                    ),
                    NavigationDestination(
                      icon: Icon(Icons.sensors_outlined),
                      selectedIcon: Icon(Icons.sensors),
                      label: 'SENSORS',
                    ),
                    NavigationDestination(
                      icon: Icon(Icons.settings_outlined),
                      selectedIcon: Icon(Icons.settings),
                      label: 'CONFIG',
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Text('BOTTOM NAVIGATION BAR', style: text.labelMedium),
                const SizedBox(height: 8),
                BottomNavigationBar(
                  currentIndex: _bottomNavIndex,
                  onTap: (int i) => setState(() => _bottomNavIndex = i),
                  items: const <BottomNavigationBarItem>[
                    BottomNavigationBarItem(
                      icon: Icon(Icons.radar),
                      label: 'SCAN',
                    ),
                    BottomNavigationBarItem(
                      icon: Icon(Icons.bolt),
                      label: 'POWER',
                    ),
                    BottomNavigationBarItem(
                      icon: Icon(Icons.terminal),
                      label: 'LOG',
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Text('BOTTOM APP BAR', style: text.labelMedium),
                const SizedBox(height: 8),
                SizedBox(
                  height: 64,
                  child: BottomAppBar(
                    child: Row(
                      children: <Widget>[
                        IconButton(
                          onPressed: () {},
                          icon: const Icon(Icons.menu),
                        ),
                        const Spacer(),
                        IconButton(
                          onPressed: () {},
                          icon: const Icon(Icons.search),
                        ),
                        IconButton(
                          onPressed: () {},
                          icon: const Icon(Icons.more_vert),
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                Text('NAVIGATION DRAWER', style: text.labelMedium),
                const SizedBox(height: 8),
                // The drawer opens off-canvas; an inline preview also renders the
                // themed destinations so the indicator / labels are visible
                // without opening it.
                OutlinedButton.icon(
                  onPressed: () => _scaffoldKey.currentState?.openDrawer(),
                  icon: const Icon(Icons.menu_open),
                  label: const Text('OPEN DRAWER'),
                ),

                // ---- DATA ----------------------------------------------------
                const _SectionHeader('DATA'),
                Text('DATA TABLE', style: text.labelMedium),
                const SizedBox(height: 8),
                SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  // Clip the table to the chamfer so the heading / selected-row
                  // fills don't poke past the cut corners of the frame.
                  child: ClipPath(
                    clipper: ChamferClipper(cut: scheme.bevel.md),
                    child: DataTable(
                      columns: const <DataColumn>[
                        DataColumn(label: Text('NODE')),
                        DataColumn(label: Text('LOAD')),
                        DataColumn(label: Text('STATE')),
                      ],
                      rows: const <DataRow>[
                        DataRow(
                          cells: <DataCell>[
                            DataCell(Text('ALPHA')),
                            DataCell(Text('42%')),
                            DataCell(Text('ONLINE')),
                          ],
                        ),
                        DataRow(
                          cells: <DataCell>[
                            DataCell(Text('BRAVO')),
                            DataCell(Text('77%')),
                            DataCell(Text('ONLINE')),
                          ],
                        ),
                        DataRow(
                          selected: true,
                          cells: <DataCell>[
                            DataCell(Text('CHARLIE')),
                            DataCell(Text('98%')),
                            DataCell(Text('CRITICAL')),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                Text('SCROLLBAR', style: text.labelMedium),
                const SizedBox(height: 8),
                // An always-visible themed Scrollbar over a short scrolling list,
                // so the gold thumb on the inset track is demonstrable.
                SizedBox(
                  height: 96,
                  child: Scrollbar(
                    thumbVisibility: true,
                    controller: _scrollbarController,
                    child: ListView.builder(
                      controller: _scrollbarController,
                      itemCount: 20,
                      itemBuilder: (BuildContext context, int i) => ListTile(
                        dense: true,
                        leading: const Icon(Icons.circle, size: 10),
                        title: Text(
                          'TELEMETRY FRAME ${i.toString().padLeft(3, '0')}',
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                Text('LIST TILES', style: text.labelMedium),
                const SizedBox(height: 8),
                ListTile(
                  leading: const Icon(Icons.memory),
                  title: const Text('PRIMARY BUS'),
                  subtitle: const Text('Voltage nominal'),
                  trailing: const Text('12.4V'),
                  onTap: () {},
                ),
                ListTile(
                  selected: true,
                  leading: const Icon(Icons.bolt),
                  title: const Text('AUX BUS'),
                  subtitle: const Text('Selected'),
                  trailing: const Text('5.0V'),
                  onTap: () {},
                ),
                const SizedBox(height: 8),
                ExpansionTile(
                  title: const Text('DIAGNOSTICS'),
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.all(16),
                      child: Text(
                        'No faults detected across 14 subsystems.',
                        style: text.bodyMedium,
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Text('PROGRESS', style: text.labelMedium),
                const SizedBox(height: 8),
                const AurisProgressBar(value: 0.66),
                const SizedBox(height: 12),
                Row(
                  children: <Widget>[
                    const SizedBox(
                      width: 28,
                      height: 28,
                      child: CircularProgressIndicator(strokeWidth: 3),
                    ),
                    const SizedBox(width: 16),
                    Text('SYNCING…', style: text.bodyMedium),
                  ],
                ),
                const SizedBox(height: 16),
                Text('BADGES', style: text.labelMedium),
                const SizedBox(height: 8),
                Row(
                  children: <Widget>[
                    Badge(
                      label: const Text('3'),
                      child: Icon(
                        Icons.notifications_outlined,
                        color: scheme.primaryActive,
                      ),
                    ),
                    const SizedBox(width: 24),
                    const Badge(
                      label: Text('SYNC'),
                      child: Icon(Icons.cloud_outlined),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Text('CAROUSEL', style: text.labelMedium),
                const SizedBox(height: 8),
                // A themed CarouselView — chamfered items on the inset surface.
                SizedBox(
                  height: 120,
                  child: CarouselView(
                    itemExtent: _carouselItemExtent,
                    itemSnapping: true,
                    children: <Widget>[
                      for (final String label in <String>[
                        'SECTOR A',
                        'SECTOR B',
                        'SECTOR C',
                        'SECTOR D',
                      ])
                        // Pin the label to the full itemExtent so the trailing
                        // partially-visible item lays out at a stable width and
                        // the carousel clips the sliver cleanly — otherwise the
                        // squeezed item's text reflows to one letter per line.
                        // Left-align so a squeezed item shows the start of its
                        // label ("SEC…") rather than the middle.
                        OverflowBox(
                          maxWidth: _carouselItemExtent,
                          alignment: Alignment.centerLeft,
                          child: Container(
                            width: _carouselItemExtent,
                            alignment: Alignment.centerLeft,
                            padding: const EdgeInsets.only(left: 24),
                            child: Text(label, style: text.titleMedium),
                          ),
                        ),
                    ],
                  ),
                ),
                const SizedBox(height: 16),
                Text('STEPPER', style: text.labelMedium),
                const SizedBox(height: 8),
                SizedBox(
                  height: 280,
                  child: Stepper(
                    currentStep: _step,
                    // Replace Material's default round step icons with the
                    // chamfered AurisStepIndicator so the stepper matches the
                    // HUD aesthetic (§spec:custom-widgets).
                    stepIconBuilder: (int stepIndex, StepState state) {
                      final AurisStepState s = switch (state) {
                        StepState.complete => AurisStepState.complete,
                        StepState.error => AurisStepState.error,
                        _ =>
                          stepIndex == _step
                              ? AurisStepState.active
                              : AurisStepState.inactive,
                      };
                      return AurisStepIndicator(
                        step: stepIndex + 1,
                        state: s,
                        size: 24,
                      );
                    },
                    onStepTapped: (int s) => setState(() => _step = s),
                    controlsBuilder:
                        (BuildContext context, ControlsDetails details) {
                          return Padding(
                            padding: const EdgeInsets.only(top: 12),
                            child: Row(
                              children: <Widget>[
                                FilledButton(
                                  onPressed: details.onStepContinue,
                                  child: const Text('NEXT'),
                                ),
                                const SizedBox(width: 8),
                                TextButton(
                                  onPressed: details.onStepCancel,
                                  child: const Text('BACK'),
                                ),
                              ],
                            ),
                          );
                        },
                    onStepContinue: () =>
                        setState(() => _step = (_step + 1).clamp(0, 2)),
                    onStepCancel: () =>
                        setState(() => _step = (_step - 1).clamp(0, 2)),
                    steps: const <Step>[
                      Step(
                        title: Text('CALIBRATE'),
                        content: Text('Align sensors.'),
                        state: StepState.complete,
                        isActive: true,
                      ),
                      Step(
                        title: Text('PRIME'),
                        content: Text('Spin up the reactor.'),
                        isActive: true,
                      ),
                      Step(
                        title: Text('ENGAGE'),
                        content: Text('Commit to launch.'),
                      ),
                    ],
                  ),
                ),

                // ---- BADGES (CUSTOM) ----------------------------------------
                const _SectionHeader('BADGES (HUD)'),
                const Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: <Widget>[
                    AurisBadge('ONLINE', variant: AurisBadgeVariant.success),
                    AurisBadge('ARMED', variant: AurisBadgeVariant.gold),
                    AurisBadge('SYNC', variant: AurisBadgeVariant.amber),
                    AurisBadge('LINK', variant: AurisBadgeVariant.slate),
                    AurisBadge('FAULT', variant: AurisBadgeVariant.danger),
                    AurisBadge('OFFLINE', variant: AurisBadgeVariant.inactive),
                  ],
                ),

                // ---- PANELS -------------------------------------------------
                const _SectionHeader('PANELS'),
                const AurisPanel(
                  title: 'Reactor Core',
                  code: 'SYS-01',
                  child: Text(
                    'Chamfered titled panel with bracket-flanked header and a '
                    'status code.',
                  ),
                ),
                const SizedBox(height: 16),
                const AurisPanel(
                  title: 'Priority Channel',
                  code: 'ACCENT',
                  accent: true,
                  child: Text(
                    'Accent mode: gold border and a subtle glow mark this panel '
                    'as emphasized.',
                  ),
                ),

                // ---- NOTIFICATIONS ------------------------------------------
                const _SectionHeader('NOTIFICATIONS'),
                const AurisNotification(
                  title: 'Uplink Established',
                  message: 'Telemetry stream is nominal.',
                  variant: AurisNotificationVariant.info,
                ),
                const SizedBox(height: 12),
                const AurisNotification(
                  title: 'Calibration Complete',
                  message: 'All 14 subsystems passed.',
                  variant: AurisNotificationVariant.success,
                ),
                const SizedBox(height: 12),
                const AurisNotification(
                  title: 'Power Reserve Low',
                  message: 'Auxiliary cells at 18%.',
                  code: 'W-204',
                  variant: AurisNotificationVariant.warning,
                ),
                const SizedBox(height: 12),
                if (_showError)
                  AurisNotification(
                    title: 'Containment Breach',
                    message: 'Sector 7 isolation failed.',
                    code: 'E-911',
                    variant: AurisNotificationVariant.error,
                    onDismiss: () => setState(() => _showError = false),
                  )
                else
                  OutlinedButton(
                    onPressed: () => setState(() => _showError = true),
                    child: const Text('RESTORE ALERT'),
                  ),

                // ---- DATA ROWS ----------------------------------------------
                const _SectionHeader('DATA ROWS'),
                const AurisDataRow(label: 'Core Temp', value: '412 K'),
                const AurisDataRow(label: 'Output', value: '82.4 MW'),
                const AurisDataRow(
                  label: 'Field Strength',
                  value: 'CRITICAL',
                  highlight: true,
                ),
                const AurisDataRow(
                  label: 'Coolant Loop',
                  value: 'NOMINAL',
                  trailing: AurisBadge(
                    'OK',
                    variant: AurisBadgeVariant.success,
                  ),
                ),

                // ---- STAT CARDS ---------------------------------------------
                const _SectionHeader('STAT CARDS'),
                const Row(
                  children: <Widget>[
                    Expanded(
                      child: AurisStatCard(
                        label: 'Throughput',
                        value: '94.2',
                        unit: 'GB/s',
                        delta: '+2.4%',
                      ),
                    ),
                    SizedBox(width: 12),
                    Expanded(
                      child: AurisStatCard(
                        label: 'Latency',
                        value: '12',
                        unit: 'ms',
                        delta: '-3.1%',
                        deltaPositiveIsGood: false,
                      ),
                    ),
                  ],
                ),

                // ---- ORNAMENTS ----------------------------------------------
                const _SectionHeader('ORNAMENTS'),
                Text('HEX CLUSTER', style: text.labelMedium),
                const SizedBox(height: 8),
                const SizedBox(
                  height: 96,
                  width: double.infinity,
                  child: AurisHexOrnament(),
                ),
                const SizedBox(height: 16),
                Text('SCAN BRACKET', style: text.labelMedium),
                const SizedBox(height: 8),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    AurisScanBracket(
                      child: Padding(
                        padding: const EdgeInsets.all(16),
                        child: Text('TARGET', style: text.bodyMedium),
                      ),
                    ),
                    AurisScanBracket(
                      pulse: true,
                      color: scheme.dangerBright,
                      child: Padding(
                        padding: const EdgeInsets.all(16),
                        child: Text('LOCK', style: text.bodyMedium),
                      ),
                    ),
                  ],
                ),

                // ---- SWITCHES -----------------------------------------------
                const _SectionHeader('SWITCHES'),
                AurisSwitch(
                  value: _aurisSwitch,
                  label: 'Primary reactor',
                  statusLabels: const ('OFFLINE', 'ONLINE'),
                  onChanged: (bool v) => setState(() => _aurisSwitch = v),
                ),
                const SizedBox(height: 16),
                AurisSwitch(
                  value: _aurisSwitchAlt,
                  label: 'Auto-stabilizer',
                  statusLabels: const ('MANUAL', 'AUTO'),
                  onChanged: (bool v) => setState(() => _aurisSwitchAlt = v),
                ),
                const SizedBox(height: 16),
                const AurisSwitch(
                  value: true,
                  label: 'Locked override',
                  statusLabels: ('OFF', 'ON'),
                  onChanged: null,
                ),

                // ---- PROGRESS -----------------------------------------------
                const _SectionHeader('PROGRESS'),
                AurisProgressBar.animated(
                  value: _progress,
                  label: 'SHIELD INTEGRITY',
                  valueLabel: '${(_progress * 100).round()} / 100',
                ),
                const SizedBox(height: 16),
                const AurisProgressBar(
                  value: 0.7,
                  label: 'COOLANT (SECONDARY)',
                  valueLabel: '70 / 100',
                  variant: AurisProgressVariant.secondary,
                ),
                const SizedBox(height: 16),
                const AurisProgressBar(
                  value: 0.92,
                  label: 'CORE TEMP (CRITICAL)',
                  valueLabel: '92 / 100',
                  variant: AurisProgressVariant.danger,
                ),
                const SizedBox(height: 12),
                Row(
                  children: <Widget>[
                    Expanded(
                      child: Slider(
                        value: _progress,
                        onChanged: (double v) => setState(() => _progress = v),
                      ),
                    ),
                  ],
                ),

                // ---- TERMINAL -----------------------------------------------
                const _SectionHeader('TERMINAL'),
                AurisTerminal(title: 'SYSTEM LOG', code: 'LIVE', lines: _log),

                // ---- SELECT -------------------------------------------------
                const _SectionHeader('SELECT'),
                AurisSelect<String>(
                  value: _aurisSelect,
                  placeholder: 'CHOOSE CHANNEL',
                  options: const <AurisSelectOption<String>>[
                    AurisSelectOption<String>(value: 'alpha', label: 'Alpha'),
                    AurisSelectOption<String>(value: 'beta', label: 'Beta'),
                    AurisSelectOption<String>(value: 'gamma', label: 'Gamma'),
                    AurisSelectOption<String>(value: 'delta', label: 'Delta'),
                  ],
                  onChanged: (String v) => setState(() => _aurisSelect = v),
                ),

                // ---- STEP INDICATOR -----------------------------------------
                const _SectionHeader('STEP INDICATOR'),
                const Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    AurisStepIndicator(step: 1, state: AurisStepState.complete),
                    AurisStepIndicator(step: 2, state: AurisStepState.active),
                    AurisStepIndicator(step: 3, state: AurisStepState.inactive),
                    AurisStepIndicator(step: 4, state: AurisStepState.error),
                  ],
                ),
                const SizedBox(height: 24),
              ],
            ),
          ),
        ),
      ),
    );
  }

  /// Open a chamfered, flat dialog (theme-driven; no per-call styling).
  Future<void> _showDialog() {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('CONFIRM LAUNCH'),
          content: const Text(
            'This commits the sequence and cannot be aborted.',
          ),
          actions: <Widget>[
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('CANCEL'),
            ),
            FilledButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('LAUNCH'),
            ),
          ],
        );
      },
    );
  }

  /// Show a themed [MaterialBanner] — inset surface, gold action, mono content.
  void _showBanner() {
    final ScaffoldMessengerState messenger = ScaffoldMessenger.of(context);
    messenger.clearMaterialBanners();
    messenger.showMaterialBanner(
      MaterialBanner(
        leading: const Icon(Icons.warning_amber),
        content: const Text('REACTOR DRIFT DETECTED — REVIEW TELEMETRY'),
        actions: <Widget>[
          TextButton(
            onPressed: messenger.hideCurrentMaterialBanner,
            child: const Text('DISMISS'),
          ),
          FilledButton(
            onPressed: messenger.hideCurrentMaterialBanner,
            child: const Text('REVIEW'),
          ),
        ],
      ),
    );
  }

  /// Open the themed [DatePickerDialog] and keep the chosen date.
  Future<void> _pickDate() async {
    final DateTime now = DateTime.now();
    final DateTime? picked = await showDatePicker(
      context: context,
      initialDate: _date ?? now,
      firstDate: DateTime(now.year - 2),
      lastDate: DateTime(now.year + 2),
    );
    if (picked != null) {
      setState(() => _date = picked);
    }
  }

  /// Open the themed [TimePickerDialog] and keep the chosen time.
  Future<void> _pickTime() async {
    final TimeOfDay? picked = await showTimePicker(
      context: context,
      initialTime: _time ?? TimeOfDay.now(),
    );
    if (picked != null) {
      setState(() => _time = picked);
    }
  }

  /// Show a chamfered, flat snackbar with a gold action.
  void _showSnackBar() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: const Text('SEQUENCE ARMED'),
        action: SnackBarAction(label: 'UNDO', onPressed: () {}),
      ),
    );
  }

  /// Open a chamfered, flat modal bottom sheet.
  Future<void> _showBottomSheet() {
    return showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        final TextTheme text = Theme.of(context).textTheme;
        return Padding(
          padding: const EdgeInsets.fromLTRB(24, 8, 24, 32),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text('TELEMETRY', style: text.titleMedium),
              const SizedBox(height: 12),
              Text(
                'A chamfered modal sheet on the panel surface.',
                style: text.bodyMedium,
              ),
              const SizedBox(height: 16),
              FilledButton(
                onPressed: () => Navigator.of(context).pop(),
                child: const Text('CLOSE'),
              ),
            ],
          ),
        );
      },
    );
  }
}

/// The on-theme control that drives the live accent / bevel / glow overrides.
///
/// Accent is a row of chamfered swatch chips (default amber plus three
/// alternates); bevel and glow are TIGHT / NORMAL / BOLD segmented toggles.
/// The control itself is built from themed widgets, so it re-skins along with
/// everything else when the accent flips. It reports changes upward — it holds
/// no theme state of its own.
class _CustomizationControl extends StatelessWidget {
  const _CustomizationControl({
    required this.accent,
    required this.bevelScale,
    required this.glowScale,
    required this.brightness,
    required this.onAccentChanged,
    required this.onBevelChanged,
    required this.onGlowChanged,
    required this.onBrightnessChanged,
  });

  final Color? accent;
  final double bevelScale;
  final double glowScale;
  final Brightness brightness;
  final ValueChanged<Color?> onAccentChanged;
  final ValueChanged<double> onBevelChanged;
  final ValueChanged<double> onGlowChanged;
  final ValueChanged<Brightness> onBrightnessChanged;

  @override
  Widget build(BuildContext context) {
    final AurisScheme scheme = Theme.of(context).extension<AurisScheme>()!;
    final TextTheme text = Theme.of(context).textTheme;

    Widget label(String s) => Text(s, style: text.labelMedium);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        label('MODE'),
        const SizedBox(height: 8),
        SegmentedButton<Brightness>(
          showSelectedIcon: false,
          segments: const <ButtonSegment<Brightness>>[
            ButtonSegment<Brightness>(
              value: Brightness.dark,
              label: Text('DARK'),
            ),
            ButtonSegment<Brightness>(
              value: Brightness.light,
              label: Text('LIGHT'),
            ),
          ],
          selected: <Brightness>{brightness},
          onSelectionChanged: (Set<Brightness> s) =>
              onBrightnessChanged(s.first),
        ),
        const SizedBox(height: 16),
        label('ACCENT'),
        const SizedBox(height: 8),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: <Widget>[
            for (final _AccentOption option in _accentOptions)
              _AccentSwatch(
                option: option,
                selected: option.color == accent,
                onTap: () => onAccentChanged(option.color),
              ),
          ],
        ),
        const SizedBox(height: 16),
        label('BEVEL'),
        const SizedBox(height: 8),
        _ScalePicker(value: bevelScale, onChanged: onBevelChanged),
        const SizedBox(height: 16),
        Row(
          children: <Widget>[
            label('GLOW'),
            const SizedBox(width: 12),
            Text(
              '${glowScale.toStringAsFixed(1)}×',
              style: TextStyle(
                fontFamily: AurisTokens.fontMono,
                fontSize: 12,
                color: scheme.primaryActive,
              ),
            ),
          ],
        ),
        // Glow is a continuous scalar (0 = none, 3 = strong), unlike the stepped
        // bevel control, so its effect can be dialled and watched on the tile.
        Slider(value: glowScale, max: 3, onChanged: onGlowChanged),
        const SizedBox(height: 8),
        // A live preview tile so the three knobs are legible on their own,
        // without scrolling to find a glowing/chamfered element: its corner cut
        // tracks BEVEL, its fill + glow tint track ACCENT, and the halo size
        // tracks GLOW. The tile is a solid accent fill with NO border so the
        // glow halo is not visually swallowed by a same-coloured outline.
        Row(
          children: <Widget>[
            AurisContainer(
              cut: scheme.bevel.lg,
              width: 96,
              height: 56,
              fill: scheme.primaryActive,
              borderWidth: 0,
              depth: scheme.depthActive,
              alignment: Alignment.center,
              child: Text(
                'PREVIEW',
                style: TextStyle(
                  fontFamily: AurisTokens.fontMono,
                  fontSize: 11,
                  letterSpacing: AurisTokens.trackingLabel,
                  color: scheme.onPrimary,
                ),
              ),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Text(
                'Flip any control — the tile and every section below recolor '
                'and re-shape from the resolved scheme.',
                style: text.bodySmall?.copyWith(color: scheme.textMid),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

/// A single chamfered accent swatch: a colored chip that selects its accent.
class _AccentSwatch extends StatelessWidget {
  const _AccentSwatch({
    required this.option,
    required this.selected,
    required this.onTap,
  });

  final _AccentOption option;
  final bool selected;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    final AurisScheme scheme = Theme.of(context).extension<AurisScheme>()!;
    // The swatch shows the accent it actually selects — the RESOLVED active
    // rung, not the raw literal. Resolving each option's accent fresh for the
    // current brightness means the chip reflects the same correction the kit
    // applies on selection: bright on dark, but darkened-for-AA on light (so a
    // teal/magenta/green chip shows the deep variant it really becomes, and AMBER
    // shows the canonical bronze). Without this the chips advertise the bright
    // literals while selecting them yields the deeper contrast-corrected color.
    final Color swatch = AurisScheme.resolve(
      brightness: Theme.of(context).brightness,
      accent: option.color,
    ).primaryActive;

    return GestureDetector(
      onTap: onTap,
      child: AurisContainer(
        cut: scheme.bevel.sm,
        fill: scheme.surfaceInset,
        borderColor: selected ? scheme.primaryActive : scheme.borderBright,
        depth: selected ? scheme.depthActive : null,
        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            AurisContainer(cut: 2, width: 14, height: 14, fill: swatch),
            const SizedBox(width: 8),
            Text(
              option.label,
              style: TextStyle(
                fontFamily: AurisTokens.fontMono,
                fontSize: 12,
                letterSpacing: AurisTokens.trackingLabel,
                color: selected ? scheme.textBright : scheme.textMid,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// A TIGHT / NORMAL / BOLD segmented toggle for a scale override.
class _ScalePicker extends StatelessWidget {
  const _ScalePicker({required this.value, required this.onChanged});

  final double value;
  final ValueChanged<double> onChanged;

  @override
  Widget build(BuildContext context) {
    return SegmentedButton<double>(
      showSelectedIcon: false,
      segments: <ButtonSegment<double>>[
        for (final _ScaleStep step in _scaleSteps)
          ButtonSegment<double>(value: step.value, label: Text(step.label)),
      ],
      selected: <double>{value},
      onSelectionChanged: (Set<double> s) => onChanged(s.first),
    );
  }
}

/// A monospace, uppercase, amber section header per §spec:showcase. Reads its
/// color from the resolved [AurisScheme] so it tracks accent / brightness.
class _SectionHeader extends StatelessWidget {
  const _SectionHeader(this.label);

  final String label;

  @override
  Widget build(BuildContext context) {
    final AurisScheme scheme = Theme.of(context).extension<AurisScheme>()!;
    return Padding(
      padding: const EdgeInsets.only(top: 8, bottom: 16),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Text(
            label.toUpperCase(),
            style: TextStyle(
              fontFamily: AurisTokens.fontMono,
              fontSize: 13,
              letterSpacing: AurisTokens.trackingLabel,
              color: scheme.primaryDim,
            ),
          ),
          const SizedBox(width: 12),
          Expanded(child: Container(height: 1, color: scheme.borderResting)),
        ],
      ),
    );
  }
}

/// An on-theme banner that makes the two cross-cutting accessibility behaviors
/// demonstrable in the showcase (§road:polish-showcase-verification):
///
/// - It reads the live `MediaQuery.disableAnimations` (the OS reduce-motion
///   setting) and reports the current state, so a reviewer can toggle the OS
///   setting, reload, and see the showcase honor it (animated widgets render
///   their end state).
/// - It prompts the reviewer to Tab through the controls and watch the gold
///   focus ring travel across every interactive element.
class _AccessibilityBanner extends StatelessWidget {
  const _AccessibilityBanner();

  @override
  Widget build(BuildContext context) {
    final bool reduceMotion = MediaQuery.disableAnimationsOf(context);
    return AurisNotification(
      title: 'ACCESSIBILITY',
      code: reduceMotion ? 'MOTION:REDUCED' : 'MOTION:FULL',
      variant: reduceMotion
          ? AurisNotificationVariant.success
          : AurisNotificationVariant.info,
      message: reduceMotion
          ? 'Reduced motion is ON (OS setting): animations render their end '
                'state with no running controllers. Press Tab to move the gold '
                'focus ring across the controls below.'
          : 'Reduced motion is OFF. Enable your OS reduce-motion setting and '
                'reload to see animations snap to their end state. Press Tab to '
                'move the gold focus ring across the controls below.',
    );
  }
}
39
likes
160
points
398
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter UI kit and Material 3 theme with a warm amber-on-near-black, chamfered sci-fi aesthetic — a drop-in ThemeData plus a library of custom HUD widgets.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on auris