import 'package:flutter/material.dart'; import '../../l10n/gen/app_localizations.dart'; import '../../state/tutorial_notifier.dart'; import 'season_background.dart' show debugDisableLoopingAnimations; /// Non-blocking guidance overlay: dim veil, message bubble, animated hand on /// the drag step, skip always available. Input still reaches the game so the /// player advances by actually doing the action. class TutorialOverlay extends StatefulWidget { const TutorialOverlay({ super.key, required this.step, required this.handFrom, required this.handTo, required this.onSkip, required this.onDismissHud, }); final TutorialStep step; /// Hand animation path in this overlay's local coordinates /// (tray slot 0 → suggested board anchor). Only used on dragPiece. final Offset handFrom; final Offset handTo; final VoidCallback onSkip; final VoidCallback onDismissHud; @override State createState() => _TutorialOverlayState(); } class _TutorialOverlayState extends State with SingleTickerProviderStateMixin { late final AnimationController _hand = AnimationController( vsync: this, duration: const Duration(milliseconds: 1400), ); @override void initState() { super.initState(); if (!debugDisableLoopingAnimations) _hand.repeat(); } @override void dispose() { _hand.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final message = switch (widget.step) { TutorialStep.dragPiece => l10n.tutorialDrag, TutorialStep.clearLine => l10n.tutorialClear, TutorialStep.explainHud => l10n.tutorialHud, }; return Stack( children: [ // Veil that lets touches through. IgnorePointer( child: Container(color: Colors.black.withValues(alpha: 0.25)), ), if (widget.step == TutorialStep.dragPiece) Positioned.fill( child: IgnorePointer( child: AnimatedBuilder( animation: _hand, builder: (context, _) { final t = Curves.easeInOut.transform(_hand.value); final pos = Offset.lerp(widget.handFrom, widget.handTo, t)!; final fade = _hand.value < 0.9 ? 1.0 : (1 - _hand.value) * 10; return Stack( children: [ Transform.translate( offset: pos, child: Opacity( opacity: fade.clamp(0.0, 1.0), child: const Icon(Icons.touch_app, size: 44, color: Colors.white), ), ), ], ); }, ), ), ), Positioned( top: 70, left: 24, right: 24, child: Card( color: const Color(0xEE1C2340), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( message, textAlign: TextAlign.center, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, ), ), if (widget.step == TutorialStep.explainHud) ...[ const SizedBox(height: 10), FilledButton( onPressed: widget.onDismissHud, child: Text(l10n.gotIt), ), ], ], ), ), ), ), Positioned( top: 8, right: 8, child: TextButton( onPressed: widget.onSkip, child: Text(l10n.skip, style: const TextStyle(color: Colors.white54)), ), ), ], ); } }