import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../theme/autopilot_theme.dart'; import '../../theme/theme_provider.dart'; import '../../theme/theme_registry.dart'; /// Appearance settings screen — "Ajustes → Apariencia". /// /// Shows a 4-card grid where each card is a 200×120 px miniature preview /// of the theme (compass rose colours + a sample DODGE button). /// /// Tapping a card applies the theme **immediately** with a 400 ms transition. /// /// ## Triple-tap shortcut /// The AR-Autopilot logo in the topbar supports triple-tap to cycle through /// all 4 themes without opening this screen. Implement the gesture in the /// topbar widget, calling `provider.setTheme(nextId)`. /// /// ## Sprint 5 placeholders /// "Auto day/night" and "Ambient light sensor" toggles are rendered but /// disabled — they are implemented in Sprint 5. class AppearanceSettingsScreen extends StatelessWidget { const AppearanceSettingsScreen({super.key}); static const String routeName = '/settings/appearance'; @override Widget build(BuildContext context) { final provider = context.watch(); final theme = provider.current; return Scaffold( backgroundColor: theme.background, appBar: AppBar( backgroundColor: theme.backgroundMid, foregroundColor: theme.textMain, elevation: 0, title: Text( 'Apariencia', style: TextStyle(color: theme.textMain, fontWeight: FontWeight.w600), ), ), body: AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, decoration: theme.backgroundDecoration, child: ListView( padding: const EdgeInsets.all(20), children: [ _SectionLabel(label: 'Tema visual', theme: theme), const SizedBox(height: 12), // ── 4-card theme selector grid ─────────────────────────────── Row( crossAxisAlignment: CrossAxisAlignment.start, children: ThemeRegistry.all.map((t) { return Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: _ThemeCard( theme: t, isSelected: t.id == theme.id, onTap: () => provider.setTheme(t.id), ), ), ); }).toList(), ), const SizedBox(height: 28), Divider(color: theme.panelBorder, thickness: 1, height: 1), const SizedBox(height: 20), // ── Sprint 5 toggles (disabled placeholders) ───────────────── _ToggleRow( theme: theme, label: 'Cambio automático día/noche', subtitle: 'De día usa "Claro", de noche usa el seleccionado arriba.', value: false, onChanged: null, // Sprint 5 ), const SizedBox(height: 16), _ToggleRow( theme: theme, label: 'Sensor de luz ambiental', subtitle: 'Si el display tiene sensor, ajusta automáticamente entre claro y el oscuro.', value: false, onChanged: null, // Sprint 5 ), const SizedBox(height: 32), ], ), ), ); } } // ── Private widgets ─────────────────────────────────────────────────────────── class _SectionLabel extends StatelessWidget { const _SectionLabel({required this.label, required this.theme}); final String label; final AutopilotTheme theme; @override Widget build(BuildContext context) => Text( label.toUpperCase(), style: TextStyle( color: theme.textMuted, fontSize: 11, letterSpacing: 1.2, fontWeight: FontWeight.w600, ), ); } class _ThemeCard extends StatelessWidget { const _ThemeCard({ required this.theme, required this.isSelected, required this.onTap, }); final AutopilotTheme theme; final bool isSelected; final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, height: 120, decoration: BoxDecoration( gradient: theme.panelBackground, borderRadius: BorderRadius.circular(8), border: Border.all( color: isSelected ? theme.accentMid : theme.panelBorder, width: isSelected ? 2 : 1, ), boxShadow: isSelected ? theme.glowShadow(theme.accentGlowColor, theme.accentGlowRadius) : null, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _MiniCompass(theme: theme), const SizedBox(height: 6), Text( theme.displayName, style: TextStyle( color: theme.textMain, fontSize: 9, fontWeight: FontWeight.w600, letterSpacing: 0.3, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 5), AnimatedContainer( duration: const Duration(milliseconds: 400), width: 7, height: 7, decoration: BoxDecoration( shape: BoxShape.circle, color: isSelected ? theme.accentMid : theme.textDisabled, ), ), ], ), ), ); } } /// 40×40 miniature compass preview for the theme card. class _MiniCompass extends StatelessWidget { const _MiniCompass({required this.theme}); final AutopilotTheme theme; @override Widget build(BuildContext context) => SizedBox( width: 40, height: 40, child: CustomPaint(painter: _MiniCompassPainter(theme: theme)), ); } class _MiniCompassPainter extends CustomPainter { const _MiniCompassPainter({required this.theme}); final AutopilotTheme theme; @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final r = size.width / 2 - 2; // Outer ring canvas.drawCircle( center, r, Paint() ..color = theme.accentMid ..style = PaintingStyle.stroke ..strokeWidth = 1.5, ); // Heading arc (accent colour) canvas.drawArc( Rect.fromCircle(center: center, radius: r - 4), -1.57, // top 1.3, false, Paint() ..color = theme.accentLight ..style = PaintingStyle.stroke ..strokeWidth = 2.5 ..strokeCap = StrokeCap.round, ); // North mark (warm colour — nautical convention) canvas.drawArc( Rect.fromCircle(center: center, radius: r), -1.65, 0.4, false, Paint() ..color = theme.northColor ..style = PaintingStyle.stroke ..strokeWidth = 3 ..strokeCap = StrokeCap.round, ); // Set-point tick (operator intent — always amber) final sx = center.dx + (r - 3) * 0.65; final sy = center.dy - (r - 3) * 0.65; final ex = center.dx + r * 0.65; final ey = center.dy - r * 0.65; canvas.drawLine( Offset(sx, sy), Offset(ex, ey), Paint() ..color = theme.setLight ..strokeWidth = 2.5 ..strokeCap = StrokeCap.round, ); } @override bool shouldRepaint(_MiniCompassPainter old) => old.theme != theme; } class _ToggleRow extends StatelessWidget { const _ToggleRow({ required this.theme, required this.label, required this.subtitle, required this.value, required this.onChanged, }); final AutopilotTheme theme; final String label; final String subtitle; final bool value; final ValueChanged? onChanged; @override Widget build(BuildContext context) { final enabled = onChanged != null; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( color: enabled ? theme.textMain : theme.textDisabled, fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( subtitle, style: TextStyle(color: theme.textMuted, fontSize: 12), ), ], ), ), const SizedBox(width: 12), Switch( value: value, onChanged: onChanged, activeColor: theme.accentMid, ), ], ); } }