diff --git a/lib/screens/auth/onboarding_page.dart b/lib/screens/auth/onboarding_page.dart index cf905c6..bc2069c 100644 --- a/lib/screens/auth/onboarding_page.dart +++ b/lib/screens/auth/onboarding_page.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../extension/screenSize.dart'; import '../../gen/assets.gen.dart'; import '../../res/colors.dart'; +import '../../widgets/language_selection_dialog.dart'; import 'login_page.dart'; class OnboardingPage extends StatefulWidget { @@ -16,6 +18,10 @@ class _OnboardingPageState extends State { int currentIndex = 0; final PageController _pageController = PageController(); + String _currentLanguage = 'πŸ‡ΊπŸ‡² English'; + String _currentFlag = 'assets/icons/usa circle.svg'; + final GlobalKey _languageKey = GlobalKey(); + final List imageAssets = [ Assets.images.ounboarding1.path, Assets.images.frame.path, @@ -35,6 +41,7 @@ class _OnboardingPageState extends State { curve: Curves.easeInOut, ); } else { + _markOnboardingComplete(); Navigator.push( context, MaterialPageRoute(builder: (context) => LoginPage()), @@ -42,6 +49,16 @@ class _OnboardingPageState extends State { } } + Future _markOnboardingComplete() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('hasSeenOnboarding', true); + } + + Future _resetOnboarding() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('hasSeenOnboarding'); + } + void _back() { if (currentIndex > 0) { _pageController.previousPage( @@ -59,6 +76,109 @@ class _OnboardingPageState extends State { return Scaffold( body: Column( children: [ + Container( + padding: EdgeInsets.fromLTRB( + width / 15, + height / 20, + width / 15, + 0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(width: 50), + GestureDetector( + key: _languageKey, + onTap: () { + showLanguageSelectionOverlay(context, _currentLanguage, ( + language, + flag, + ) { + setState(() { + _currentLanguage = language; + _currentFlag = flag; + }); + }, _languageKey); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: ClipRRect( + key: ValueKey(_currentFlag), + borderRadius: BorderRadius.circular(4), + child: + _currentFlag == 'placeholder' + ? Container( + width: 24, + height: 18, + decoration: BoxDecoration( + color: AppColors.primary.withOpacity(0.1), + borderRadius: BorderRadius.circular(4), + ), + child: Icon( + Icons.flag, + color: AppColors.primary, + size: 12, + ), + ) + : _currentFlag.endsWith('.svg') + ? SvgPicture.asset( + _currentFlag, + width: 24, + height: 18, + ) + : Image.asset( + _currentFlag, + width: 24, + height: 18, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + width: 24, + height: 18, + decoration: BoxDecoration( + color: AppColors.greyBorder + .withOpacity(0.2), + borderRadius: BorderRadius.circular( + 4, + ), + ), + child: Icon( + Icons.flag, + color: AppColors.textSecondary, + size: 12, + ), + ); + }, + ), + ), + ), + const SizedBox(width: 8), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: Text( + _currentLanguage, + key: ValueKey(_currentLanguage), + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary, + ), + ), + ), + const SizedBox(width: 8), + SvgPicture.asset( + Assets.icons.arrowRight.path, + color: AppColors.textPrimary, + ), + ], + ), + ), + ], + ), + ), Expanded( child: PageView.builder( controller: _pageController, @@ -72,7 +192,8 @@ class _OnboardingPageState extends State { return Padding( padding: EdgeInsets.fromLTRB( width / 15, - height / 40, + height / + 60, width / 15, height / 30, ), diff --git a/lib/screens/mains/hunt/hunt.dart b/lib/screens/mains/hunt/hunt.dart index 0f6f926..06f5764 100644 --- a/lib/screens/mains/hunt/hunt.dart +++ b/lib/screens/mains/hunt/hunt.dart @@ -115,6 +115,19 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi void _onCardSelected(HuntCard card) async { final huntProvider = Provider.of(context, listen: false); + // If the same card is selected, deselect it + if (huntProvider.selectedCard?.id == card.id) { + // Play sound for deselection too + await GameSoundService.playCardFlipSound(); + Vibration.hasVibrator().then((hasVibrator) { + if (hasVibrator == true) { + Vibration.vibrate(duration: 50); // Lighter vibration for deselect + } + }); + huntProvider.deselectCard(); + return; + } + await GameSoundService.playCardFlipSound(); Vibration.hasVibrator().then((hasVibrator) { if (hasVibrator == true) { @@ -243,6 +256,7 @@ class _HuntContentState extends State<_HuntContent> with TickerProviderStateMixi return Padding( padding: const EdgeInsets.only(bottom: 12), child: HuntCardWidget( + key: ValueKey(card.id), card: card, onTap: () => _onCardSelected(card), isSelected: huntProvider.selectedCard?.id == card.id, diff --git a/lib/screens/mains/hunt/providers/hunt_provider.dart b/lib/screens/mains/hunt/providers/hunt_provider.dart index f7d3124..d71eb1a 100644 --- a/lib/screens/mains/hunt/providers/hunt_provider.dart +++ b/lib/screens/mains/hunt/providers/hunt_provider.dart @@ -62,6 +62,11 @@ class HuntState extends ChangeNotifier { notifyListeners(); } + void deselectCard() { + _selectedCard = null; + notifyListeners(); + } + void startHunt() { if (_selectedCard != null) { _huntStartTime = DateTime.now(); diff --git a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart index 411536f..88fa775 100644 --- a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart +++ b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart @@ -84,7 +84,21 @@ class _HuntCardWidgetState extends State @override void didUpdateWidget(HuntCardWidget oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.isFlipped != oldWidget.isFlipped) { + + // Handle selection state changes first + if (widget.isSelected != oldWidget.isSelected) { + if (widget.isSelected) { + // Card is being selected + _flipController.forward(); + } else { + // Card is being deselected - animate back to front + _flipController.reverse(); + _scaleController.reverse(); + } + } + + // Handle flip animation based on isFlipped property if not already handled by selection + if (widget.isFlipped != oldWidget.isFlipped && widget.isSelected == oldWidget.isSelected) { if (widget.isFlipped) { _flipController.forward(); } else { diff --git a/lib/simple_auth_gate.dart b/lib/simple_auth_gate.dart index 9200f22..9ee77db 100644 --- a/lib/simple_auth_gate.dart +++ b/lib/simple_auth_gate.dart @@ -3,12 +3,42 @@ import 'package:flutter/material.dart'; import 'package:lba/screens/auth/login_page.dart'; import 'package:lba/screens/auth/onboarding_page.dart'; import 'package:lba/screens/mains/navigation/navigation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -class SimpleAuthGate extends StatelessWidget { +class SimpleAuthGate extends StatefulWidget { const SimpleAuthGate({super.key}); + @override + State createState() => _SimpleAuthGateState(); +} + +class _SimpleAuthGateState extends State { + bool? _isFirstTime; + + @override + void initState() { + super.initState(); + _checkFirstTime(); + } + + Future _checkFirstTime() async { + final prefs = await SharedPreferences.getInstance(); + final hasSeenOnboarding = prefs.getBool('hasSeenOnboarding') ?? false; + setState(() { + _isFirstTime = !hasSeenOnboarding; + }); + } + @override Widget build(BuildContext context) { + if (_isFirstTime == null) { + return const Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } + return StreamBuilder( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { @@ -42,12 +72,8 @@ class SimpleAuthGate extends StatelessWidget { return const MainScreen(); } - return _shouldShowOnboarding() ? const OnboardingPage() : const LoginPage(); + return _isFirstTime! ? const OnboardingPage() : const LoginPage(); }, ); } - - bool _shouldShowOnboarding() { - return false; - } } diff --git a/lib/widgets/language_selection_dialog.dart b/lib/widgets/language_selection_dialog.dart index 327252f..fef34ab 100644 --- a/lib/widgets/language_selection_dialog.dart +++ b/lib/widgets/language_selection_dialog.dart @@ -104,6 +104,17 @@ class _LanguageSelectionOverlayState extends State Widget build(BuildContext context) { final targetPosition = _getTargetPosition(); final screenWidth = MediaQuery.of(context).size.width; + + final overlayWidth = screenWidth * 0.45; + + double? leftPosition; + double? rightPosition; + + if (targetPosition.dx > screenWidth * 0.7) { + rightPosition = 16; + } else { + rightPosition = 24; + } return AnimatedBuilder( animation: _animationController, @@ -113,56 +124,59 @@ class _LanguageSelectionOverlayState extends State child: GestureDetector( onTap: _closeOverlay, behavior: HitTestBehavior.translucent, - child: Container( - width: double.infinity, - height: double.infinity, - child: Stack( - children: [ - Positioned( - right: targetPosition.dx + 10, - top: targetPosition.dy + 40, - child: Transform.scale( - scale: _scaleAnimation.value, - child: Opacity( - opacity: _opacityAnimation.value, - child: Container( - width: - screenWidth * 0.45, // Less than half screen width - decoration: BoxDecoration( - color: AppColors.surface, - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(16), - bottomRight: Radius.circular(16), - topLeft: Radius.circular(16), - ), - boxShadow: [ - BoxShadow( - color: AppColors.shadowColor, - blurRadius: 15, - spreadRadius: 3, - offset: const Offset(0, 5), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Stack( + children: [ + Positioned( + left: leftPosition, + right: rightPosition, + top: targetPosition.dy + 40, + child: Transform.scale( + scale: _scaleAnimation.value, + child: Opacity( + opacity: _opacityAnimation.value, + child: Container( + width: overlayWidth, + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(16), + bottomRight: Radius.circular(16), + topLeft: Radius.circular(16), ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: - languages.map((language) { - final isSelected = - _selectedLanguage == language['name']; - return _buildLanguageOption( - language['name']!, - language['code']!, - language['flag']!, - isSelected, - ); - }).toList(), + boxShadow: [ + BoxShadow( + color: AppColors.shadowColor, + blurRadius: 15, + spreadRadius: 3, + offset: const Offset(0, 5), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: + languages.map((language) { + final isSelected = + _selectedLanguage == language['name']; + return _buildLanguageOption( + language['name']!, + language['code']!, + language['flag']!, + isSelected, + ); + }).toList(), + ), ), ), ), ), - ), - ], + ], + ), ), ), ), @@ -177,7 +191,7 @@ class _LanguageSelectionOverlayState extends State String flag, bool isSelected, ) { - print('Building language option: $name with flag: $flag'); // Debug log + print('Building language option: $name with flag: $flag'); return Material( color: Colors.transparent, child: InkWell(