import 'package:flutter/material.dart'; import 'package:lba/res/colors.dart'; class AnimatedRankBadge extends StatefulWidget { final int rank; final int totalPoints; final String userName; final bool showAnimation; final VoidCallback? onTap; const AnimatedRankBadge({ super.key, required this.rank, required this.totalPoints, required this.userName, this.showAnimation = true, this.onTap, }); @override State createState() => _AnimatedRankBadgeState(); } class _AnimatedRankBadgeState extends State with TickerProviderStateMixin { late AnimationController _pulseController; late AnimationController _shimmerController; late Animation _pulseAnimation; late Animation _shimmerAnimation; @override void initState() { super.initState(); _pulseController = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); _shimmerController = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this, ); _pulseAnimation = Tween( begin: 1.0, end: 1.1, ).animate(CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, )); _shimmerAnimation = Tween( begin: -1.0, end: 1.0, ).animate(CurvedAnimation( parent: _shimmerController, curve: Curves.easeInOut, )); if (widget.showAnimation) { _pulseController.repeat(reverse: true); _shimmerController.repeat(); } } @override void dispose() { _pulseController.dispose(); _shimmerController.dispose(); super.dispose(); } Color _getRankColor() { switch (widget.rank) { case 1: return const Color(0xFFFFD700); case 2: return const Color(0xFFC0C0C0); case 3: return const Color(0xFFCD7F32); default: if (widget.rank <= 10) { return AppColors.primary; } else { return AppColors.textSecondary; } } } IconData _getRankIcon() { switch (widget.rank) { case 1: return Icons.emoji_events_rounded; case 2: return Icons.emoji_events_outlined; case 3: return Icons.emoji_events_outlined; default: if (widget.rank <= 10) { return Icons.military_tech_rounded; } else { return Icons.person_rounded; } } } String _getRankSuffix() { if (widget.rank == 1) return 'st'; if (widget.rank == 2) return 'nd'; if (widget.rank == 3) return 'rd'; return 'th'; } @override Widget build(BuildContext context) { final isTopThree = widget.rank <= 3; final rankColor = _getRankColor(); return GestureDetector( onTap: widget.onTap, child: AnimatedBuilder( animation: widget.showAnimation ? _pulseAnimation : AlwaysStoppedAnimation(1.0), builder: (context, child) { return Transform.scale( scale: widget.showAnimation ? _pulseAnimation.value : 1.0, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isTopThree ? [ rankColor, rankColor.withOpacity(0.8), ] : [ AppColors.cardBackground, AppColors.cardBackground.withOpacity(0.8), ], ), borderRadius: BorderRadius.circular(20), border: Border.all( color: rankColor.withOpacity(0.3), width: 1.5, ), boxShadow: [ BoxShadow( color: rankColor.withOpacity(0.3), blurRadius: isTopThree ? 12 : 6, offset: const Offset(0, 4), spreadRadius: isTopThree ? 2 : 0, ), ], ), child: Stack( children: [ if (isTopThree && widget.showAnimation) AnimatedBuilder( animation: _shimmerAnimation, builder: (context, child) { return Positioned.fill( child: ClipRRect( borderRadius: BorderRadius.circular(18), child: CustomPaint( painter: ShimmerPainter( progress: _shimmerAnimation.value, color: Colors.white.withOpacity(0.3), ), size: Size.infinite, ), ), ); }, ), Row( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isTopThree ? Colors.white.withOpacity(0.2) : rankColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( _getRankIcon(), color: isTopThree ? Colors.white : rankColor, size: 20, ), ), const SizedBox(width: 12), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( '${widget.rank}${_getRankSuffix()}', style: TextStyle( color: isTopThree ? Colors.white : AppColors.textPrimary, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 4), Text( 'Place', style: TextStyle( color: isTopThree ? Colors.white70 : AppColors.textSecondary, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: 2), Row( children: [ Text( widget.userName, style: TextStyle( color: isTopThree ? Colors.white70 : AppColors.textSecondary, fontSize: 12, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: isTopThree ? Colors.white.withOpacity(0.2) : AppColors.confirmButton.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.stars_rounded, color: isTopThree ? Colors.white : AppColors.confirmButton, size: 12, ), const SizedBox(width: 2), Text( '${widget.totalPoints}', style: TextStyle( color: isTopThree ? Colors.white : AppColors.confirmButton, fontSize: 10, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ], ), ], ), ], ), ), ); }, ), ); } } class ShimmerPainter extends CustomPainter { final double progress; final Color color; ShimmerPainter({required this.progress, required this.color}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..shader = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.transparent, color, Colors.transparent, ], stops: const [0.0, 0.5, 1.0], ).createShader(Rect.fromLTWH( size.width * progress - size.width * 0.3, 0, size.width * 0.6, size.height, )); canvas.drawRect(Offset.zero & size, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }