import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../../../core/theme/app_colors.dart'; /// Premium channel card with Netflix-style design /// /// Features: /// - Glassmorphism background /// - Thumbnail/logo with fallback /// - Live badge with pulse animation /// - EPG overlay /// - Hover/tap effects class ChannelCard extends StatefulWidget { final String name; final String? iconUrl; final String? currentProgram; final bool isLive; final VoidCallback? onTap; final double? width; final double? height; const ChannelCard({ super.key, required this.name, this.iconUrl, this.currentProgram, this.isLive = true, this.onTap, this.width, this.height, }); @override State createState() => _ChannelCardState(); } class _ChannelCardState extends State with SingleTickerProviderStateMixin { bool _isHovered = false; late AnimationController _pulseController; late Animation _pulseAnimation; @override void initState() { super.initState(); _pulseController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), )..repeat(reverse: true); _pulseAnimation = Tween(begin: 0.8, end: 1.0).animate( CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut), ); } @override void dispose() { _pulseController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; return MouseRegion( onEnter: (_) => setState(() => _isHovered = true), onExit: (_) => setState(() => _isHovered = false), child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeOutCubic, width: widget.width ?? 180, height: widget.height ?? 120, transform: Matrix4.identity()..scale(_isHovered ? 1.05 : 1.0), transformAlignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isDark ? [ AppColors.surfaceDark.withOpacity(0.95), AppColors.surfaceVariantDark.withOpacity(0.9), ] : [ Colors.white.withOpacity(0.95), AppColors.surfaceVariantLight.withOpacity(0.9), ], ), border: Border.all( color: _isHovered ? AppColors.primary.withOpacity(0.5) : (isDark ? AppColors.borderDark : AppColors.borderLight), width: _isHovered ? 1.5 : 1, ), boxShadow: _isHovered ? [ BoxShadow( color: AppColors.primary.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 8), ), ] : [ BoxShadow( color: Colors.black.withOpacity(isDark ? 0.3 : 0.1), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(11), child: Stack( fit: StackFit.expand, children: [ // Channel logo/thumbnail _buildChannelImage(isDark), // Gradient overlay for text readability Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.black.withOpacity(0.7), ], stops: const [0.4, 1.0], ), ), ), ), // Live badge if (widget.isLive) Positioned( top: 8, right: 8, child: _buildLiveBadge(), ), // Channel info overlay Positioned( left: 10, right: 10, bottom: 10, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.name, style: const TextStyle( color: Colors.white, fontSize: 13, fontWeight: FontWeight.w600, shadows: [ Shadow( color: Colors.black54, blurRadius: 4, ), ], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (widget.currentProgram != null) ...[ const SizedBox(height: 2), Text( widget.currentProgram!, style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 11, fontWeight: FontWeight.w400, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ], ), ), ], ), ), ), ), ); } Widget _buildChannelImage(bool isDark) { if (widget.iconUrl != null && widget.iconUrl!.isNotEmpty) { return CachedNetworkImage( imageUrl: widget.iconUrl!, fit: BoxFit.cover, placeholder: (context, url) => _buildPlaceholder(isDark), errorWidget: (context, url, error) => _buildPlaceholder(isDark), ); } return _buildPlaceholder(isDark); } Widget _buildPlaceholder(bool isDark) { return Container( color: isDark ? AppColors.surfaceVariantDark : AppColors.surfaceVariantLight, child: Center( child: Icon( Icons.live_tv_rounded, size: 36, color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ); } Widget _buildLiveBadge() { return AnimatedBuilder( animation: _pulseAnimation, builder: (context, child) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: AppColors.live.withOpacity(_pulseAnimation.value), borderRadius: BorderRadius.circular(4), boxShadow: [ BoxShadow( color: AppColors.live.withOpacity(0.4 * _pulseAnimation.value), blurRadius: 8, spreadRadius: 1, ), ], ), child: child, ); }, child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.circle, size: 6, color: Colors.white, ), SizedBox(width: 4), Text( 'LIVE', style: TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w700, letterSpacing: 0.5, ), ), ], ), ); } }