import 'dart:async'; import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:lba/gen/assets.gen.dart'; import 'package:provider/provider.dart'; import 'package:vibration/vibration.dart'; import 'package:lba/res/colors.dart'; import 'providers/hunt_provider.dart'; import 'services/game_sound_service.dart'; import 'widgets/hunt_card_widget.dart'; import 'widgets/leaderboard_widget.dart'; import 'models/hunt_card.dart'; class Hunt extends StatelessWidget { const Hunt({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => HuntState()..initializeGame(), child: const _HuntContent(), ); } } class _HuntContent extends StatefulWidget { const _HuntContent(); @override State<_HuntContent> createState() => _HuntContentState(); } class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixin { late AnimationController _mainAnimationController; late AnimationController _cardAnimationController; late AnimationController _confettiController; late AnimationController _backgroundGradientController; late Animation _fadeAnimation; late Animation _slideAnimation; late Animation _gradientAnimation; Timer? _locationTimer; bool _showLeaderboard = false; @override void initState() { super.initState(); _setupAnimations(); _initializeGame(); } void _setupAnimations() { _mainAnimationController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _cardAnimationController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _confettiController = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this, ); _backgroundGradientController = AnimationController( duration: const Duration(seconds: 8), vsync: this, )..repeat(); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _mainAnimationController, curve: Curves.easeInOut, )); _slideAnimation = Tween( begin: const Offset(0, 0.3), end: Offset.zero, ).animate(CurvedAnimation( parent: _mainAnimationController, curve: Curves.easeOutCubic, )); _gradientAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _backgroundGradientController, curve: Curves.easeInOut, )); _mainAnimationController.forward(); } void _initializeGame() { } @override void dispose() { _mainAnimationController.dispose(); _cardAnimationController.dispose(); _confettiController.dispose(); _backgroundGradientController.dispose(); _locationTimer?.cancel(); GameSoundService.dispose(); super.dispose(); } void _onCardSelected(HuntCard card) async { final huntProvider = Provider.of(context, listen: false); await GameSoundService.playCardFlipSound(); Vibration.hasVibrator().then((hasVibrator) { if (hasVibrator == true) { Vibration.vibrate(duration: 100); } }); // Toggle the flip state of the card huntProvider.toggleCardFlip(card.id); } @override Widget build(BuildContext context) { return Scaffold( body: AnimatedBuilder( animation: _gradientAnimation, builder: (context, child) { final t = _gradientAnimation.value; return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: AppColors.isDarkMode ? [ Color.lerp(const Color(0xFF0F172A), const Color(0xFF1E293B), (sin(t * pi * 2) + 1) / 2)!, Color.lerp(const Color(0xFF1E293B), const Color(0xFF334155), (cos(t * pi * 2 + pi / 3) + 1) / 2)!, Color.lerp(const Color(0xFF334155), const Color(0xFF475569), (sin(t * pi * 2 + pi / 2) + 1) / 2)!, Color.lerp(const Color(0xFF475569), const Color(0xFF0F172A), (cos(t * pi * 2 + pi) + 1) / 2)!, ] : [ Color.lerp(const Color(0xFFFFFFFF), const Color(0xFFF8F9FA), (sin(t * pi * 2) + 1) / 2)!, Color.lerp(const Color(0xFFF8F9FA), const Color(0xFFE9ECEF), (cos(t * pi * 2 + pi / 3) + 1) / 2)!, Color.lerp(const Color(0xFFE9ECEF), const Color(0xFFF8F9FA), (sin(t * pi * 2 + pi / 2) + 1) / 2)!, Color.lerp(const Color(0xFFF8F9FA), const Color(0xFFFFFFFF), (cos(t * pi * 2 + pi) + 1) / 2)!, ], stops: const [0.0, 0.3, 0.7, 1.0], ), ), child: Consumer( builder: (context, huntProvider, child) { return Stack( children: [ SafeArea( child: FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: _buildMainContent(huntProvider), ), ), ), if (_showLeaderboard) Positioned.fill( child: Container( color: Colors.black.withOpacity(0.5), child: Align( alignment: Alignment.bottomCenter, child: LeaderboardWidget( entries: huntProvider.leaderboard, userPoints: huntProvider.userPoints, onClose: () { setState(() { _showLeaderboard = false; }); }, ), ), ), ), ], ); }, ), ); }, ), ); } Widget _buildMainContent(HuntState huntProvider) { return _buildCardSelection(huntProvider); } Widget _buildCardSelection(HuntState huntProvider) { return Column( children: [ _buildHeader(), Expanded( child: Scrollbar( thumbVisibility: false, trackVisibility: false, radius: const Radius.circular(6), thickness: 4, child: CustomScrollView( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics(), ), slivers: [ SliverToBoxAdapter( child: Column( children: [ const SizedBox(height: 16), _buildPointsDisplay(huntProvider), const SizedBox(height: 18), _buildInstructions(), const SizedBox(height: 16), ], ), ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final card = huntProvider.cards[index]; return Padding( padding: const EdgeInsets.only(bottom: 12), child: HuntCardWidget( card: card, onTap: () => _onCardSelected(card), isSelected: huntProvider.selectedCard?.id == card.id, isFlipped: huntProvider.isCardFlipped(card.id), ), ); }, childCount: huntProvider.cards.length, ), ), ), const SliverToBoxAdapter( child: SizedBox(height: 20), ), ], ), ), ), ], ); } Widget _buildHeader() { return ClipRRect( borderRadius: BorderRadius.circular(16), child: BackdropFilter( filter: ImageFilter.blur( sigmaX: AppColors.isDarkMode ? 0 : 10.0, sigmaY: AppColors.isDarkMode ? 0 : 10.0, ), child: Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white.withOpacity(0.4), borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white.withOpacity(0.2), width: 1, ), boxShadow: [ BoxShadow( color: AppColors.isDarkMode ? Colors.black.withOpacity(0.3) : Colors.black.withOpacity(0.04), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.1) : const Color(0xFF1976D2).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: SvgPicture.asset( Assets.icons.searchNormal.path, color: AppColors.isDarkMode ? Colors.white : const Color(0xFF1976D2), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hunt', style: TextStyle( color: AppColors.textPrimary, fontSize: 22, fontWeight: FontWeight.w700, letterSpacing: -0.5, ), ), Text( 'Discover & Collect', style: TextStyle( color: AppColors.textSecondary, fontSize: 13, fontWeight: FontWeight.w500, ), ), ], ), ), GestureDetector( onTap: () { setState(() { _showLeaderboard = true; }); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.1) : const Color(0xFF1976D2).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ SvgPicture.asset( Assets.icons.ranking.path, color: AppColors.isDarkMode ? Colors.white : const Color(0xFF1976D2), ), const SizedBox(width: 6), Text( 'Ranks', style: TextStyle( color: AppColors.isDarkMode ? Colors.white : const Color(0xFF1976D2), fontSize: 14, fontWeight: FontWeight.w600, ), ), ], ), ), ), ], ), ), ), ); } Widget _buildPointsDisplay(HuntState huntProvider) { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.10) : Colors.white.withOpacity(0.6), borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.15) : Colors.white.withOpacity(0.3), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildStatItem( icon: Assets.icons.medalStar.path, label: 'Total Points', value: '${huntProvider.userPoints}', color: AppColors.isDarkMode ? const Color(0xFF64B5F6) : const Color(0xFF1976D2), ), Container( height: 32, width: 1, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ AppColors.divider.withOpacity(0), AppColors.divider.withOpacity(0.6), AppColors.divider.withOpacity(0), ], ), ), ), _buildStatItem( icon: Assets.icons.cup.path, label: 'Rank', value: '#${huntProvider.currentUserRank}', color: AppColors.isDarkMode ? const Color(0xFF81C784) : const Color(0xFF388E3C), ), ], ), ); } Widget _buildStatItem({ required String icon, required String label, required String value, required Color color, }) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color.withOpacity(0.15), borderRadius: BorderRadius.circular(12), ), child: SvgPicture.asset(icon, color: color,), ), const SizedBox(height: 8), Text( value, style: TextStyle( color: AppColors.textPrimary, fontSize: 18, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 2), Text( label, style: TextStyle( color: AppColors.textSecondary, fontSize: 11, fontWeight: FontWeight.w400, ), ), ], ); } Widget _buildInstructions() { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.08) : Colors.white.withOpacity(0.7), borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.isDarkMode ? Colors.white.withOpacity(0.12) : Colors.white.withOpacity(0.4), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 15, offset: const Offset(0, 8), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColors.isDarkMode ? const Color(0xFF64B5F6).withOpacity(0.2) : const Color(0xFF1976D2).withOpacity(0.12), borderRadius: BorderRadius.circular(10), ), child: Icon( Icons.psychology_rounded, color: AppColors.isDarkMode ? const Color(0xFF64B5F6) : const Color(0xFF1976D2), size: 18, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Gaming Quest Mode', style: TextStyle( color: AppColors.textPrimary, fontSize: 14, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 3), Text( 'Tap any card to reveal your mystery quest! Solve riddles, find locations, and earn points.', style: TextStyle( color: AppColors.textSecondary, fontSize: 11, fontWeight: FontWeight.w400, height: 1.3, ), ), ], ), ), ], ), ); } }