import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart' as shimmer; import '../estado/estado_radio.dart'; import '../tema/pluriwave_theme.dart'; import '../widgets/pluri_glass_surface.dart'; import '../widgets/pluri_icon.dart'; import 'package:pluriwave/widgets/tarjeta_emisora.dart'; /// Pantalla principal: emisoras populares y por género. class PantallaInicio extends StatefulWidget { const PantallaInicio({super.key}); @override State createState() => _PantallaInicioState(); } class _PantallaInicioState extends State { static const _generos = [ 'pop', 'rock', 'jazz', 'classical', 'electronic', 'news', 'talk', 'hip-hop', 'country', 'metal', 'reggae', 'latin', ]; String? _generoSeleccionado; @override Widget build(BuildContext context) { final estado = context.watch(); final theme = Theme.of(context); final t = context.pluriTokens; return RefreshIndicator( onRefresh: estado.cargarPopulares, child: CustomScrollView( slivers: [ SliverToBoxAdapter(child: _heroHeader(context)), SliverToBoxAdapter(child: _seccionTendencias(estado, theme)), SliverToBoxAdapter(child: _chipGeneros(context, theme)), if (estado.error != null) SliverToBoxAdapter(child: _errorBanner(estado, theme)), SliverPadding( padding: EdgeInsets.symmetric(horizontal: t.spacingMd), sliver: _gridEmisoras(estado), ), ], ), ); } Widget _heroHeader(BuildContext context) { final t = context.pluriTokens; final theme = Theme.of(context); return Padding( padding: EdgeInsets.fromLTRB( t.spacingMd, t.spacingSm, t.spacingMd, t.spacingSm, ), child: PluriGlassSurface( borderRadius: BorderRadius.circular(t.radiusLg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const PluriIcon( glyph: PluriIconGlyph.home, variant: PluriIconVariant.activeGlow, size: 30, ), const SizedBox(height: 10), Text( 'PluriWave', style: theme.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.w700, ), ), Text( 'Ondas vivas globales', style: theme.textTheme.titleMedium?.copyWith(color: t.warmCoral), ), ], ), ), ); } Widget _seccionTendencias(EstadoRadio estado, ThemeData theme) { return Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), child: PluriGlassSurface( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Tendencias premium', style: theme.textTheme.titleMedium), const SizedBox(height: 8), SizedBox( height: 56, child: estado.cargandoPopulares ? ListView.separated( scrollDirection: Axis.horizontal, itemCount: 5, separatorBuilder: (_, __) => const SizedBox(width: 8), itemBuilder: (_, __) => _ChipShimmer(theme: theme), ) : ListView.separated( scrollDirection: Axis.horizontal, itemCount: estado.tendencias.length, separatorBuilder: (_, __) => const SizedBox(width: 8), itemBuilder: (context, i) { final e = estado.tendencias[i]; return ActionChip( avatar: const Icon( Icons.graphic_eq_rounded, size: 18, ), label: Text(e.nombre, maxLines: 1), onPressed: () => context.read().reproducir(e), ).animate().fadeIn(delay: (i * 50).ms); }, ), ), ], ), ), ); } Widget _chipGeneros(BuildContext context, ThemeData theme) { return Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), child: PluriGlassSurface( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Géneros', style: theme.textTheme.titleMedium), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 4, children: _generos.map((g) { final seleccionado = _generoSeleccionado == g; return FilterChip( label: Text(g), selected: seleccionado, onSelected: (_) { setState(() { _generoSeleccionado = seleccionado ? null : g; }); if (!seleccionado) { context.read().buscar(tag: g); } else { context.read().cargarPopulares(); } }, ); }).toList(), ), ], ), ), ); } Widget _errorBanner(EstadoRadio estado, ThemeData theme) { return Padding( padding: const EdgeInsets.all(16), child: PluriGlassSurface( padding: const EdgeInsets.all(12), child: Row( children: [ Icon(Icons.wifi_off, color: theme.colorScheme.error), const SizedBox(width: 8), Expanded(child: Text(estado.error!)), TextButton( onPressed: estado.cargarPopulares, child: const Text('Reintentar'), ), ], ), ), ); } Widget _gridEmisoras(EstadoRadio estado) { final emisoras = _generoSeleccionado != null ? estado.resultadosBusqueda : estado.emisorasInicio; final cargando = estado.cargandoPopulares || (_generoSeleccionado != null && estado.cargandoBusqueda); if (cargando) { return SliverGrid( delegate: SliverChildBuilderDelegate( (_, __) => const TarjetaEmisoraShimmer(), childCount: 12, ), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 0.85, crossAxisSpacing: 8, mainAxisSpacing: 8, ), ); } if (emisoras.isEmpty) { return const SliverFillRemaining( child: Center(child: Text('No hay emisoras disponibles')), ); } return SliverGrid( delegate: SliverChildBuilderDelegate( (context, i) => TarjetaEmisora( emisora: emisoras[i], onTap: () => context.read().reproducir(emisoras[i]), ).animate().fadeIn(delay: (i * 30).ms).slideY(begin: 0.1), childCount: emisoras.length, ), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 0.85, crossAxisSpacing: 8, mainAxisSpacing: 8, ), ); } } class _ChipShimmer extends StatelessWidget { final ThemeData theme; const _ChipShimmer({required this.theme}); @override Widget build(BuildContext context) { return shimmer.Shimmer.fromColors( baseColor: theme.colorScheme.surfaceContainerHighest, highlightColor: theme.colorScheme.surface, child: Container( width: 120, height: 56, decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), ), ), ); } }