import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shake/shake.dart'; import '../l10n/app_localizations.dart'; import '../game/zentap_game.dart'; import '../utils/colors.dart'; import '../utils/settings_manager.dart'; import '../utils/haptic_utils.dart'; import '../utils/theme_notifier.dart'; import '../game/audio/audio_manager.dart'; import 'components/animated_background.dart'; import '../utils/app_lifecycle_manager.dart'; class GameScreen extends StatefulWidget { final bool isZenMode; const GameScreen({super.key, required this.isZenMode}); @override State createState() => _GameScreenState(); } class _GameScreenState extends State { late ZenTapGame game; bool _isPaused = false; ShakeDetector? _shakeDetector; final GlobalKey _backgroundKey = GlobalKey(); @override void initState() { super.initState(); game = ZenTapGame(); game.setZenMode(widget.isZenMode); // Register the game instance for app lifecycle management AppLifecycleManager.instance.setCurrentGame(game); // Hide system UI for immersive experience SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } @override void didChangeDependencies() { super.didChangeDependencies(); // Set localized strings for the game final l10n = AppLocalizations.of(context)!; game.setLocalizedStrings( zenMode: l10n.zenModeGame, relaxationPoints: l10n.relaxationPoints, time: l10n.time, ); // Initialize shake detector only on mobile platforms _initializeShakeDetector(); // Start ingame music when entering game WidgetsBinding.instance.addPostFrameCallback((_) { AudioManager().playIngameMusic(); }); } void _initializeShakeDetector() async { if (_shakeDetector != null) return; // Already initialized try { // Only initialize shake detector on mobile platforms if (Theme.of(context).platform == TargetPlatform.android || Theme.of(context).platform == TargetPlatform.iOS) { _shakeDetector = ShakeDetector.autoStart( onPhoneShake: (ShakeEvent event) => _onPhoneShake(), minimumShakeCount: 1, shakeSlopTimeMS: 500, shakeCountResetTime: 3000, shakeThresholdGravity: 2.7, ); } } catch (e) { // Shake detection not supported on this platform print('Shake detection not supported: $e'); } } @override void dispose() { try { _shakeDetector?.stopListening(); } catch (e) { // Shake detector might not be initialized on desktop } // Unregister the game instance from lifecycle management AppLifecycleManager.instance.setCurrentGame(null); // Restore system UI SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.dispose(); } void _onPhoneShake() async { if (!_isPaused) { // Trigger background shake animation final backgroundState = _backgroundKey.currentState; if (backgroundState != null) { (backgroundState as dynamic).triggerShake(); } // Trigger game shake effects (spawn bubbles and shake existing ones) game.handleShake(); // Haptic feedback if (SettingsManager.isHapticsEnabled) { await HapticUtils.vibrate(duration: 100); } } } @override Widget build(BuildContext context) { return ThemeAwareBuilder( builder: (context, theme) { return Scaffold( backgroundColor: ZenColors.currentAppBackground, body: OrientationBuilder( builder: (context, orientation) { return KeyboardListener( focusNode: FocusNode()..requestFocus(), onKeyEvent: (event) { // Add keyboard shortcut for shake simulation on desktop if (event.logicalKey == LogicalKeyboardKey.space && event is KeyDownEvent) { _onPhoneShake(); } }, child: Stack( children: [ // Animated Background AnimatedBackground( key: _backgroundKey, isZenMode: widget.isZenMode, onShake: () {}, // Background handles its own shake animation child: Container(), // Empty container just for background ), // Game Widget with tap detection (transparent background) GestureDetector( onTapDown: (details) async { // Add haptic feedback on tap if (SettingsManager.isHapticsEnabled) { await HapticUtils.vibrate(duration: 30); } // Convert screen position to game position final position = Vector2( details.localPosition.dx, details.localPosition.dy, ); game.handleTap(position); }, child: GameWidget.controlled( gameFactory: () => game, ), ), // Top UI Overlay - adaptive to orientation SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), child: orientation == Orientation.portrait ? _buildPortraitUI() : _buildLandscapeUI(), ), ), // Pause Overlay if (_isPaused) _buildPauseOverlay(), ], ), ); }, ), ); }, ); } Widget _buildPortraitUI() { return Row( children: [ // Back Button IconButton( onPressed: _showExitDialog, icon: Icon( Icons.arrow_back, color: ZenColors.currentPrimaryText, size: 28, ), style: IconButton.styleFrom( backgroundColor: ZenColors.black.withValues(alpha: 0.3), shape: const CircleBorder(), ), ), const Spacer(), // Mode Indicator Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: ZenColors.black.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(20), ), child: Text( widget.isZenMode ? AppLocalizations.of(context)!.zenModeGame.toUpperCase() : AppLocalizations.of(context)!.playModeGame.toUpperCase(), style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 14, fontWeight: FontWeight.w600, letterSpacing: 1.0, ), ), ), const Spacer(), // Pause Button IconButton( onPressed: _togglePause, icon: Icon( _isPaused ? Icons.play_arrow : Icons.pause, color: ZenColors.currentPrimaryText, size: 28, ), style: IconButton.styleFrom( backgroundColor: ZenColors.black.withValues(alpha: 0.3), shape: const CircleBorder(), ), ), ], ); } Widget _buildLandscapeUI() { return Row( children: [ // Back Button IconButton( onPressed: _showExitDialog, icon: Icon( Icons.arrow_back, color: ZenColors.currentPrimaryText, size: 24, ), style: IconButton.styleFrom( backgroundColor: ZenColors.black.withValues(alpha: 0.3), shape: const CircleBorder(), ), ), const SizedBox(width: 16), // Mode Indicator (smaller in landscape) Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: ZenColors.black.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(16), ), child: Text( widget.isZenMode ? AppLocalizations.of(context)!.zenModeShort : AppLocalizations.of(context)!.playModeShort, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 12, fontWeight: FontWeight.w600, letterSpacing: 1.0, ), ), ), const Spacer(), // Pause Button IconButton( onPressed: _togglePause, icon: Icon( _isPaused ? Icons.play_arrow : Icons.pause, color: ZenColors.currentPrimaryText, size: 24, ), style: IconButton.styleFrom( backgroundColor: ZenColors.black.withValues(alpha: 0.3), shape: const CircleBorder(), ), ), ], ); } Widget _buildPauseOverlay() { return Container( color: ZenColors.black.withValues(alpha: 0.8), child: Center( child: Container( margin: const EdgeInsets.all(40), padding: const EdgeInsets.all(30), decoration: BoxDecoration( color: ZenColors.currentUiElements, borderRadius: BorderRadius.circular(20), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( AppLocalizations.of(context)!.paused, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 32, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), Text( AppLocalizations.of(context)!.takeAMomentToBreathe, style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: 16, ), ), const SizedBox(height: 30), // Resume Button ElevatedButton( onPressed: _togglePause, style: ElevatedButton.styleFrom( backgroundColor: ZenColors.currentButtonBackground, foregroundColor: ZenColors.currentButtonText, padding: const EdgeInsets.symmetric( horizontal: 40, vertical: 15, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( AppLocalizations.of(context)!.resume, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), ), ), ], ), ), ), ); } void _togglePause() async { if (SettingsManager.isHapticsEnabled) { await HapticUtils.vibrate(duration: 50); } setState(() { _isPaused = !_isPaused; if (_isPaused) { game.pauseGame(); } else { game.resumeGame(); } }); } void _showExitDialog() async { if (!mounted) return; if (SettingsManager.isHapticsEnabled) { await HapticUtils.vibrate(duration: 50); } if (!mounted) return; showDialog( context: context, builder: (BuildContext context) { return AlertDialog( backgroundColor: ZenColors.currentUiElements, title: Text( AppLocalizations.of(context)!.leaveGame, style: TextStyle(color: ZenColors.currentPrimaryText), ), content: Text( AppLocalizations.of(context)!.leaveGameConfirm, style: TextStyle(color: ZenColors.currentSecondaryText), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text( AppLocalizations.of(context)!.cancel, style: TextStyle(color: ZenColors.currentLinks), ), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); // Close dialog Navigator.of( context, ).pop(true); // Return to main menu with result }, style: ElevatedButton.styleFrom( backgroundColor: ZenColors.red, foregroundColor: ZenColors.white, ), child: Text(AppLocalizations.of(context)!.leave), ), ], ); }, ); } }