From 32557bb5d8e6a5eaa54b6dbec1e7f60a1553b47a Mon Sep 17 00:00:00 2001 From: mohamadmahdi jebeli Date: Thu, 11 Sep 2025 08:23:50 +0330 Subject: [PATCH] Hunt --- lib/screens/mains/hunt/hunt.dart.backup | 856 ++++++++++++++++++++++++ 1 file changed, 856 insertions(+) create mode 100644 lib/screens/mains/hunt/hunt.dart.backup diff --git a/lib/screens/mains/hunt/hunt.dart.backup b/lib/screens/mains/hunt/hunt.dart.backup new file mode 100644 index 0000000..3c7db3b --- /dev/null +++ b/lib/screens/mains/hunt/hunt.dart.backup @@ -0,0 +1,856 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:vibration/vibration.dart'; +import 'package:lba/res/colors.dart'; +import 'providers/hunt_provider.dart'; +import 'services/location_service.dart'; +import 'services/game_sound_service.dart'; +import 'widgets/hunt_card_widget.dart'; +import 'widgets/leaderboard_widget.dart'; +import 'widgets/hint_camera_widget.dart'; +import 'widgets/hunt_timer_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 Animation _fadeAnimation; + late Animation _slideAnimation; + + 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, + ); + + _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, + )); + + _mainAnimationController.forward(); + } + + void _initializeGame() { + // The initialization is now handled in the provider creation + } + + @override + void dispose() { + _mainAnimationController.dispose(); + _cardAnimationController.dispose(); + _confettiController.dispose(); + _locationTimer?.cancel(); + GameSoundService.dispose(); + super.dispose(); + } + + void _startLocationMonitoring(HuntCard card) { + _locationTimer?.cancel(); + _locationTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { + final position = await LocationService.getCurrentPosition(); + if (position != null) { + final isNearTarget = LocationService.isWithinRange( + position.latitude, + position.longitude, + card.targetLatitude, + card.targetLongitude, + rangeInMeters: 50.0, + ); + + if (isNearTarget && mounted) { + _onHuntCompleted(); + timer.cancel(); + } + } + }); + } + + void _onCardSelected(HuntCard card) async { + final huntProvider = Provider.of(context, listen: false); + + // Gaming feedback: sound + vibration + await GameSoundService.playCardFlipSound(); + Vibration.hasVibrator().then((hasVibrator) { + if (hasVibrator == true) { + Vibration.vibrate(duration: 100); + } + }); + + huntProvider.selectCard(card); + + // No dialog, just flip the card to show the riddle + } + + void _showPermissionDialog(String message) { + showDialog( + context: context, + builder: (context) => AlertDialog( + backgroundColor: AppColors.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: Text( + 'Permission Required', + style: TextStyle( + color: AppColors.textPrimary, + fontWeight: FontWeight.bold, + ), + ), + content: Text( + message, + style: TextStyle( + color: AppColors.textSecondary, + height: 1.4, + ), + ), + actions: [ + ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: const Text('OK'), + ), + ], + ), + ); + } + + void _openHintCamera() async { + final huntProvider = Provider.of(context, listen: false); + final selectedCard = huntProvider.selectedCard; + + if (selectedCard == null) return; + + final hasCameraPermission = await LocationService.checkCameraPermission(); + if (!hasCameraPermission) { + _showPermissionDialog('Camera permission is required for the hint feature.'); + return; + } + + huntProvider.setCameraPermissionGranted(true); + huntProvider.activateHintMode(); + + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => HintCameraWidget( + targetLatitude: selectedCard.hintLatitude, + targetLongitude: selectedCard.hintLongitude, + hintDescription: selectedCard.hintDescription, + onHintFound: () async { + await GameSoundService.playHintFoundSound(); + }, + onClose: () { + Navigator.of(context).pop(); + huntProvider.startHunt(); // Go back to hunting mode + }, + ), + ), + ); + } + + void _onHuntCompleted() async { + final huntProvider = Provider.of(context, listen: false); + + await GameSoundService.playSuccessSound(); + await Future.delayed(const Duration(milliseconds: 500)); + await GameSoundService.playPointsEarnedSound(); + + huntProvider.completeHunt(); + _confettiController.forward(); + + _showCompletionDialog(); + } + + void _showCompletionDialog() { + final huntProvider = Provider.of(context, listen: false); + final selectedCard = huntProvider.selectedCard; + + if (selectedCard == null) return; + + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + backgroundColor: AppColors.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: Column( + children: [ + Icon( + Icons.celebration, + color: AppColors.confirmButton, + size: 48, + ), + const SizedBox(height: 8), + Text( + 'Congratulations!', + style: TextStyle( + color: AppColors.textPrimary, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'You found ${selectedCard.answer}!', + style: TextStyle( + color: AppColors.primary, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.confirmButton.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.stars_rounded, + color: AppColors.confirmButton, + size: 24, + ), + const SizedBox(width: 8), + Text( + '+${selectedCard.points} Points', + style: TextStyle( + color: AppColors.confirmButton, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + actions: [ + Row( + children: [ + Expanded( + child: TextButton( + onPressed: () { + Navigator.of(context).pop(); + setState(() { + _showLeaderboard = true; + }); + }, + child: Text( + 'View Leaderboard', + style: TextStyle(color: AppColors.primary), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + huntProvider.resetGame(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: const Text('Play Again'), + ), + ), + ], + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.scaffoldBackground, + body: Consumer( + builder: (context, huntProvider, child) { + return Stack( + children: [ + SafeArea( + child: FadeTransition( + opacity: _fadeAnimation, + child: SlideTransition( + position: _slideAnimation, + child: _buildMainContent(huntProvider), + ), + ), + ), + + // Leaderboard overlay + 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) { + // Always show card selection page, don't switch to other views + return _buildCardSelection(huntProvider); + } + + Widget _buildCardSelection(HuntState huntProvider) { + return Column( + children: [ + _buildHeader(), + const SizedBox(height: 20), + _buildPointsDisplay(huntProvider), + const SizedBox(height: 24), + _buildInstructions(), + const SizedBox(height: 20), + Expanded( + child: _buildCardGrid(huntProvider), + ), + ], + ); + } + + + + Widget _buildHeader() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + AppColors.primary.withOpacity(0.1), + AppColors.primary.withOpacity(0.05), + ], + ), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: AppColors.primary.withOpacity(0.2), + width: 1, + ), + boxShadow: [ + BoxShadow( + color: AppColors.primary.withOpacity(0.1), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.2), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.explore_rounded, + color: AppColors.primary, + size: 20, + ), + ), + const SizedBox(width: 12), + Text( + 'Treasure Hunt', + style: TextStyle( + color: AppColors.textPrimary, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + '🗺️ Discover hidden gems in your city', + style: TextStyle( + color: AppColors.textSecondary, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + const SizedBox(width: 16), + GestureDetector( + onTap: () { + setState(() { + _showLeaderboard = true; + }); + }, + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + AppColors.primary, + AppColors.primary.withOpacity(0.8), + ], + ), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: AppColors.primary.withOpacity(0.3), + blurRadius: 8, + offset: const Offset(0, 3), + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.leaderboard_rounded, + color: Colors.white, + size: 20, + ), + const SizedBox(width: 6), + Text( + 'Ranks', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), + ], + ), + ); + } + + Widget _buildHuntHeader(HuntState huntProvider) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + children: [ + IconButton( + onPressed: () => huntProvider.resetGame(), + icon: Icon( + Icons.arrow_back_rounded, + color: AppColors.textPrimary, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Active Hunt', + style: TextStyle( + color: AppColors.textPrimary, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + if (huntProvider.hasTimeLeft) + HuntTimerWidget( + timeRemaining: huntProvider.timeRemaining, + isActive: huntProvider.gameState == HuntGameState.huntingActive, + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildPointsDisplay(HuntState huntProvider) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: AppColors.cardBackground, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: AppColors.shadowColor, + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildStatItem( + icon: Icons.stars_rounded, + label: 'Total Points', + value: '${huntProvider.userPoints}', + color: AppColors.confirmButton, + ), + Container( + height: 40, + width: 1, + color: AppColors.divider, + ), + _buildStatItem( + icon: Icons.emoji_events, + label: 'Rank', + value: '#${huntProvider.currentUserRank}', + color: AppColors.primary, + ), + ], + ), + ); + } + + Widget _buildStatItem({ + required IconData icon, + required String label, + required String value, + required Color color, + }) { + return Column( + children: [ + Icon(icon, color: color, size: 28), + const SizedBox(height: 8), + Text( + value, + style: TextStyle( + color: AppColors.textPrimary, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Text( + label, + style: TextStyle( + color: AppColors.textSecondary, + fontSize: 12, + ), + ), + ], + ); + } + + Widget _buildInstructions() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Colors.amber.withOpacity(0.15), + Colors.orange.withOpacity(0.1), + ], + ), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.amber.withOpacity(0.3), + width: 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.amber.withOpacity(0.1), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.amber.withOpacity(0.2), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.psychology_rounded, + color: Colors.amber.shade700, + size: 18, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Gaming Quest Mode', + style: TextStyle( + color: Colors.amber.shade800, + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + 'Tap any card to reveal your mystery quest! Solve riddles, find locations, and earn points.', + style: TextStyle( + color: Colors.amber.shade700, + fontSize: 11, + fontWeight: FontWeight.w500, + height: 1.3, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildCardGrid(HuntState huntProvider) { + return ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 12), + itemCount: huntProvider.cards.length, + itemBuilder: (context, index) { + final card = huntProvider.cards[index]; + return HuntCardWidget( + card: card, + onTap: () => _onCardSelected(card), + isSelected: huntProvider.selectedCard?.id == card.id, + isFlipped: huntProvider.selectedCard?.id == card.id, + ); + }, + ); + } + + Widget _buildSelectedCard(HuntCard card) { + return HuntCardWidget( + card: card, + onTap: () {}, + isSelected: true, + isFlipped: true, + customHeight: 220, + ); + } + + Widget _buildHuntActions(HuntState huntProvider) { + return Row( + children: [ + Expanded( + child: ElevatedButton.icon( + onPressed: _openHintCamera, + icon: const Icon(Icons.camera_alt_outlined), + label: const Text('Get Hint'), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.cardBackground, + foregroundColor: AppColors.textPrimary, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide(color: AppColors.divider), + ), + ), + ), + ), + const SizedBox(width: 16), + Expanded( + child: ElevatedButton.icon( + onPressed: () { + setState(() { + _showLeaderboard = true; + }); + }, + icon: const Icon(Icons.leaderboard), + label: const Text('Leaderboard'), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + ], + ); + } + + Widget _buildHuntStatus(HuntState huntProvider) { + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: AppColors.cardBackground, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Hunt Status', + style: TextStyle( + color: AppColors.textPrimary, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + _buildStatusItem( + icon: Icons.location_on, + title: 'Location Access', + status: huntProvider.isLocationEnabled ? 'Enabled' : 'Disabled', + isActive: huntProvider.isLocationEnabled, + ), + const SizedBox(height: 12), + _buildStatusItem( + icon: Icons.camera_alt, + title: 'Camera Access', + status: huntProvider.isCameraPermissionGranted ? 'Granted' : 'Not Granted', + isActive: huntProvider.isCameraPermissionGranted, + ), + const SizedBox(height: 12), + _buildStatusItem( + icon: Icons.timer, + title: 'Time Remaining', + status: huntProvider.hasTimeLeft ? 'Active' : 'Expired', + isActive: huntProvider.hasTimeLeft, + ), + ], + ), + ); + } + + Widget _buildStatusItem({ + required IconData icon, + required String title, + required String status, + required bool isActive, + }) { + return Row( + children: [ + Icon( + icon, + color: isActive ? AppColors.confirmButton : AppColors.textSecondary, + size: 20, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + title, + style: TextStyle( + color: AppColors.textPrimary, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: isActive + ? AppColors.confirmButton.withOpacity(0.1) + : AppColors.textSecondary.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + status, + style: TextStyle( + color: isActive ? AppColors.confirmButton : AppColors.textSecondary, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ); + } +} \ No newline at end of file