import 'package:flutter/material.dart'; import '../theme/app_theme.dart'; // ── Badge de estado ─────────────────────────────────────────────────────────── class StatusBadge extends StatelessWidget { final String label; final Color backgroundColor; final Color textColor; const StatusBadge({ super.key, required this.label, this.backgroundColor = AppTheme.primaryLight, this.textColor = AppTheme.primaryDark, }); factory StatusBadge.green(String label) => StatusBadge( label: label, backgroundColor: AppTheme.primaryLight, textColor: AppTheme.primaryDark, ); factory StatusBadge.amber(String label) => StatusBadge( label: label, backgroundColor: AppTheme.amberLight, textColor: AppTheme.amber, ); factory StatusBadge.gray(String label) => StatusBadge( label: label, backgroundColor: const Color(0xFFF1EFE8), textColor: const Color(0xFF5F5E5A), ); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(AppTheme.radiusFull), ), child: Text( label, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: textColor, ), ), ); } } // ── Tarjeta base ────────────────────────────────────────────────────────────── class AppCard extends StatelessWidget { final Widget child; final EdgeInsets? padding; final Color? borderColor; final VoidCallback? onTap; const AppCard({ super.key, required this.child, this.padding, this.borderColor, this.onTap, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( width: double.infinity, padding: padding ?? const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all( color: borderColor ?? AppTheme.border, width: 0.5, ), boxShadow: AppTheme.softShadow, ), child: child, ), ); } } // ── Fila de información con ícono ───────────────────────────────────────────── class InfoRow extends StatelessWidget { final IconData icon; final String label; final String value; final Widget? trailing; const InfoRow({ super.key, required this.icon, required this.label, required this.value, this.trailing, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(10), ), child: Icon(icon, color: AppTheme.primary, size: 20), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(value, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppTheme.textPrimary)), const SizedBox(height: 2), Text(label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary)), ], ), ), if (trailing != null) trailing!, ], ), ); } } // ── Campo de formulario ─────────────────────────────────────────────────────── class FormField extends StatelessWidget { final String label; final String? hint; final TextEditingController? controller; final bool obscureText; final TextInputType? keyboardType; final String? initialValue; final Widget? suffix; final int? maxLines; const FormField({ super.key, required this.label, this.hint, this.controller, this.obscureText = false, this.keyboardType, this.initialValue, this.suffix, this.maxLines = 1, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: AppTheme.textSecondary)), const SizedBox(height: 6), TextFormField( controller: controller, initialValue: initialValue, obscureText: obscureText, keyboardType: keyboardType, maxLines: maxLines, style: const TextStyle(fontSize: 14, color: AppTheme.textPrimary), decoration: InputDecoration( hintText: hint, suffixIcon: suffix, ), ), ], ); } } // ── Sección con título ──────────────────────────────────────────────────────── class SectionTitle extends StatelessWidget { final String title; final Widget? action; const SectionTitle({super.key, required this.title, this.action}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 10), child: Row( children: [ Text( title.toUpperCase(), style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: AppTheme.textSecondary, letterSpacing: 0.8, ), ), const Spacer(), if (action != null) action!, ], ), ); } } // ── Toggle con label ────────────────────────────────────────────────────────── class LabeledSwitch extends StatelessWidget { final String label; final bool value; final ValueChanged onChanged; const LabeledSwitch({ super.key, required this.label, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ Expanded( child: Text(label, style: const TextStyle( fontSize: 14, color: AppTheme.textPrimary)), ), Switch.adaptive( value: value, onChanged: onChanged, activeColor: AppTheme.primary, ), ], ), ); } } // ── Ítem de menú ────────────────────────────────────────────────────────────── class MenuTile extends StatelessWidget { final IconData icon; final String title; final String? subtitle; final VoidCallback? onTap; final Color? iconColor; final Color? titleColor; final Widget? trailing; const MenuTile({ super.key, required this.icon, required this.title, this.subtitle, this.onTap, this.iconColor, this.titleColor, this.trailing, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusMd), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Row( children: [ Icon(icon, color: iconColor ?? AppTheme.primary, size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: titleColor ?? AppTheme.textPrimary)), if (subtitle != null) ...[ const SizedBox(height: 2), Text(subtitle!, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary)), ], ], ), ), trailing ?? const Icon(Icons.chevron_right, color: AppTheme.textSecondary, size: 18), ], ), ), ); } } // ── Bottom Nav Bar ──────────────────────────────────────────────────────────── class AppBottomNav extends StatelessWidget { final int currentIndex; final Function(int) onTap; const AppBottomNav({ super.key, required this.currentIndex, required this.onTap, }); @override Widget build(BuildContext context) { return BottomNavigationBar( currentIndex: currentIndex, onTap: onTap, type: BottomNavigationBarType.fixed, backgroundColor: AppTheme.surface, selectedItemColor: AppTheme.primary, unselectedItemColor: AppTheme.textSecondary, selectedFontSize: 11, unselectedFontSize: 11, elevation: 12, items: const [ BottomNavigationBarItem( icon: Icon(Icons.map_outlined), activeIcon: Icon(Icons.map), label: 'Mapa', ), BottomNavigationBarItem( icon: Icon(Icons.notifications_outlined), activeIcon: Icon(Icons.notifications), label: 'Alertas', ), BottomNavigationBarItem( icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: 'Mi casa', ), BottomNavigationBarItem( icon: Icon(Icons.person_outline), activeIcon: Icon(Icons.person), label: 'Perfil', ), ], ); } }