import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; import '../estado/estado_radio.dart'; import '../modelos/emisora.dart'; import '../tema/pluriwave_theme.dart'; import 'pluri_glass_surface.dart'; import 'pluri_icon.dart'; /// Tarjeta compacta para mostrar una emisora en listas y grids. /// Incluye botón de favorito visible en ambos modos. class TarjetaEmisora extends StatefulWidget { final Emisora emisora; final VoidCallback? onTap; final bool esCompacta; const TarjetaEmisora({ super.key, required this.emisora, this.onTap, this.esCompacta = false, }); @override State createState() => _TarjetaEmisoraState(); } class _TarjetaEmisoraState extends State { bool _toggling = false; Future _toggle() async { if (_toggling) return; setState(() => _toggling = true); final estado = context.read(); final esFav = await estado.toggleFavorito(widget.emisora); if (mounted) setState(() => _toggling = false); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( esFav ? '${widget.emisora.nombre} añadida a favoritos' : '${widget.emisora.nombre} eliminada de favoritos', ), duration: const Duration(seconds: 2), ), ); } } @override Widget build(BuildContext context) { final t = context.pluriTokens; return Semantics( button: widget.onTap != null, label: 'Emisora ${widget.emisora.nombre}', child: PluriGlassSurface( padding: EdgeInsets.zero, borderRadius: BorderRadius.circular( widget.esCompacta ? t.radiusMd : t.radiusLg, ), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onTap, child: widget.esCompacta ? _buildCompacta() : _buildCompleta(), ), ), ), ); } Widget _buildCompleta() { final t = context.pluriTokens; return Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( aspectRatio: 1, child: Stack( fit: StackFit.expand, children: [ _logo(60), DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.black.withValues(alpha: 0.34), ], ), ), ), Positioned( left: t.spacingSm, bottom: t.spacingSm, child: _LiveBadge(mini: true), ), ], ), ), Padding( padding: EdgeInsets.fromLTRB( t.spacingMd, t.spacingSm, t.spacingMd, t.spacingMd, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.emisora.nombre, style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w700, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), if (widget.emisora.pais != null) Padding( padding: EdgeInsets.only(top: t.spacingXs), child: Text( widget.emisora.pais!, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of( context, ).colorScheme.onSurface.withValues(alpha: 0.72), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), ], ), Positioned( top: t.spacingSm, right: t.spacingSm, child: _botonFavorito(mini: true), ), ], ); } Widget _buildCompacta() { final t = context.pluriTokens; final subtitulo = [ widget.emisora.pais, widget.emisora.idioma, ].where((s) => s != null && s.isNotEmpty).join(' · '); return Padding( padding: EdgeInsets.symmetric( horizontal: t.spacingSm, vertical: t.spacingXs, ), child: Row( children: [ Stack( alignment: Alignment.center, children: [ Container( width: 58, height: 58, decoration: BoxDecoration( shape: BoxShape.circle, gradient: SweepGradient( colors: [t.electricMagenta, const Color(0xFF20E6FF), t.warmCoral, t.electricMagenta], ), boxShadow: [BoxShadow(color: t.glowColor.withValues(alpha: 0.24), blurRadius: 22)], ), ), ClipRRect( borderRadius: BorderRadius.circular(18), child: SizedBox(width: 50, height: 50, child: _logo(24)), ), ], ), SizedBox(width: t.spacingSm), Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.emisora.nombre, style: Theme.of( context, ).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w700), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (subtitulo.isNotEmpty) Text( subtitulo, maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of( context, ).colorScheme.onSurface.withValues(alpha: 0.72), ), ), ], ), ), const SizedBox(width: 8), _LiveBadge(mini: false), _botonFavorito(mini: false), ], ), ); } Widget _botonFavorito({required bool mini}) { final t = context.pluriTokens; final esFavorito = context.select( (estado) => estado.listaFavoritos.any((e) => e.uuid == widget.emisora.uuid), ); final icono = mini ? Icon( esFavorito ? Icons.favorite_rounded : Icons.favorite_outline_rounded, color: esFavorito ? t.warmCoral : Colors.white.withValues(alpha: 0.82), size: 18, ) : PluriIcon( glyph: PluriIconGlyph.favorites, variant: esFavorito ? PluriIconVariant.activeGlow : PluriIconVariant.outline, size: 20, semanticLabel: esFavorito ? 'Quitar de favoritos' : 'Añadir a favoritos', ); return Semantics( button: true, toggled: esFavorito, label: esFavorito ? 'Quitar de favoritos' : 'Añadir a favoritos', child: Material( color: mini ? t.glassSurface : Colors.transparent, shape: const CircleBorder(), child: InkWell( customBorder: const CircleBorder(), onTap: _toggling ? null : _toggle, child: SizedBox( width: mini ? 36 : 44, height: mini ? 36 : 44, child: Center(child: icono), ), ), ), ); } Widget _logo(double iconSize) { if (widget.emisora.favicon != null && widget.emisora.favicon!.isNotEmpty) { return CachedNetworkImage( imageUrl: widget.emisora.favicon!, fit: BoxFit.cover, placeholder: (_, __) => _shimmer(), errorWidget: (_, __, ___) => _iconoFallback(iconSize), ); } return _iconoFallback(iconSize); } Widget _shimmer() { final theme = Theme.of(context); return Shimmer.fromColors( baseColor: theme.colorScheme.surfaceContainerHighest, highlightColor: theme.colorScheme.surface, child: Container(color: theme.colorScheme.surfaceContainerHighest), ); } Widget _iconoFallback(double size) { final art = _fallbackArtFor(widget.emisora.uuid); return Stack( fit: StackFit.expand, children: [ Image.asset( art, fit: BoxFit.cover, errorBuilder: (_, __, ___) => DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ context.pluriTokens.deepViolet, context.pluriTokens.electricMagenta.withValues(alpha: 0.8), ], ), ), ), ), Center( child: PluriIcon( glyph: PluriIconGlyph.player, variant: PluriIconVariant.activeGlow, size: size, semanticLabel: 'Icono de emisora', ), ), ], ); } String _fallbackArtFor(String seed) { const arts = [ 'assets/images/station_art_aurora.png', 'assets/images/station_art_cosmic.png', 'assets/images/station_art_pulse.png', 'assets/images/station_art_nova.png', ]; final index = seed.codeUnits.fold(0, (a, b) => a + b) % arts.length; return arts[index]; } } class _LiveBadge extends StatelessWidget { const _LiveBadge({required this.mini}); final bool mini; @override Widget build(BuildContext context) { final color = Theme.of(context).colorScheme.secondary; return Container( padding: EdgeInsets.symmetric(horizontal: mini ? 8 : 6, vertical: mini ? 5 : 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(999), color: Colors.black.withValues(alpha: 0.35), border: Border.all(color: color.withValues(alpha: 0.48)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.fiber_manual_record_rounded, size: mini ? 10 : 8, color: color), if (mini) ...[ const SizedBox(width: 5), Text('Live', style: Theme.of(context).textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w900)), ], ], ), ); } } /// Placeholder shimmer para listas en carga. class TarjetaEmisoraShimmer extends StatelessWidget { const TarjetaEmisoraShimmer({super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Shimmer.fromColors( baseColor: theme.colorScheme.surfaceContainerHighest, highlightColor: theme.colorScheme.surface, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( aspectRatio: 1, child: Container(color: theme.colorScheme.surfaceContainerHighest), ), const SizedBox(height: 8), Container( height: 14, color: theme.colorScheme.surfaceContainerHighest, ), const SizedBox(height: 4), Container( height: 12, width: 60, color: theme.colorScheme.surfaceContainerHighest, ), ], ), ); } }