import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:aesthetica_wallpaper/models/puzzle_game.dart'; import 'package:aesthetica_wallpaper/providers/puzzle_provider.dart'; import 'package:aesthetica_wallpaper/screens/puzzle/puzzle_complete_screen.dart'; /// 拼图游戏主界面 class PuzzleGameScreen extends StatefulWidget { final String imagePath; final GameDifficulty difficulty; final GameMode mode; const PuzzleGameScreen({ super.key, required this.imagePath, required this.difficulty, required this.mode, }); @override State createState() => _PuzzleGameScreenState(); } class _PuzzleGameScreenState extends State { bool _showPreview = false; int? _selectedPieceIndex; @override void initState() { super.initState(); // 创建游戏 WidgetsBinding.instance.addPostFrameCallback((_) { context.read().createGame( imagePath: widget.imagePath, difficulty: widget.difficulty, mode: widget.mode, ); }); } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.purple.shade50, Colors.blue.shade50], ), ), child: SafeArea( child: Consumer( builder: (context, provider, child) { final game = provider.currentGame; if (game == null) { return const Center(child: CircularProgressIndicator()); } // 游戏完成后跳转 if (game.isComplete) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => PuzzleCompleteScreen(game: game), ), ); }); } return Column( children: [ // 顶部信息栏 _buildTopBar(game), // 游戏区域 Expanded( child: Center(child: _buildPuzzleGrid(game, provider)), ), // 底部工具栏 _buildBottomBar(game, provider), ], ); }, ), ), ), ); } Widget _buildTopBar(PuzzleGame game) { return Container( padding: const EdgeInsets.all(16), child: Row( children: [ // 返回按钮 IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => _showExitDialog(), ), const Spacer(), // 难度显示 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.purple.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), ), child: Text( game.difficulty.description, style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.purple, ), ), ), const Spacer(), // 计时器 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), ), child: Row( children: [ const Icon(Icons.timer, size: 16, color: Colors.blue), const SizedBox(width: 4), Text( _formatDuration(game.elapsedTime), style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.blue, ), ), ], ), ), const SizedBox(width: 8), // 步数 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.orange.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(20), ), child: Row( children: [ const Icon( Icons.directions_walk, size: 16, color: Colors.orange, ), const SizedBox(width: 4), Text( '${game.moves}', style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.orange, ), ), ], ), ), ], ), ); } Widget _buildPuzzleGrid(PuzzleGame game, PuzzleProvider provider) { final gridSize = game.gridSize; final screenWidth = MediaQuery.of(context).size.width; final puzzleSize = screenWidth * 0.9; final pieceSize = puzzleSize / gridSize; return Stack( children: [ // 拼图网格 Container( width: puzzleSize, height: puzzleSize, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: GridView.builder( physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: gridSize, ), itemCount: game.pieces.length, itemBuilder: (context, index) { return _buildPuzzlePiece(game, index, provider, pieceSize); }, ), ), ), // 预览覆盖层 if (_showPreview) Positioned.fill( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.black.withValues(alpha: 0.3), ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Image.asset(widget.imagePath, fit: BoxFit.cover), ), ), ), ], ); } Widget _buildPuzzlePiece( PuzzleGame game, int index, PuzzleProvider provider, double size, ) { final piece = game.pieces[index]; final isSelected = _selectedPieceIndex == index; final isCorrect = piece.isCorrect; final isEmpty = game.mode == GameMode.classic && game.emptyPosition == index; return GestureDetector( onTap: () => _onPieceTap(index, game, provider), child: Container( decoration: BoxDecoration( border: Border.all( color: isSelected ? Colors.blue : isCorrect ? Colors.green.withValues(alpha: 0.3) : Colors.grey.withValues(alpha: 0.3), width: isSelected ? 3 : 1, ), color: isEmpty ? Colors.grey.shade200 : null, ), child: isEmpty ? const SizedBox.shrink() : Stack( fit: StackFit.expand, children: [ Image(image: piece.image, fit: BoxFit.cover), // 正确位置指示器 if (isCorrect) Positioned( top: 4, right: 4, child: Container( padding: const EdgeInsets.all(2), decoration: const BoxDecoration( color: Colors.green, shape: BoxShape.circle, ), child: const Icon( Icons.check, color: Colors.white, size: 12, ), ), ), ], ), ), ); } void _onPieceTap(int index, PuzzleGame game, PuzzleProvider provider) { if (game.mode == GameMode.classic) { // 滑动模式:直接移动 provider.movePiece(index); } else { // 交换模式:选择两个块 if (_selectedPieceIndex == null) { setState(() { _selectedPieceIndex = index; }); } else { provider.swapPieces(_selectedPieceIndex!, index); setState(() { _selectedPieceIndex = null; }); } } } Widget _buildBottomBar(PuzzleGame game, PuzzleProvider provider) { return Container( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 重新开始 _buildToolButton( icon: Icons.refresh, label: '重新开始', color: Colors.orange, onPressed: () => _showRestartDialog(provider), ), // 提示 _buildToolButton( icon: Icons.lightbulb, label: '提示', color: Colors.yellow.shade700, onPressed: () => provider.useHint(), ), // 预览 GestureDetector( onLongPressStart: (_) => setState(() => _showPreview = true), onLongPressEnd: (_) => setState(() => _showPreview = false), child: _buildToolButton( icon: Icons.image, label: '预览', color: Colors.blue, onPressed: () {}, ), ), // 暂停 _buildToolButton( icon: Icons.pause, label: '暂停', color: Colors.purple, onPressed: () => _showPauseDialog(), ), ], ), ); } Widget _buildToolButton({ required IconData icon, required String label, required Color color, required VoidCallback onPressed, }) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( decoration: BoxDecoration( color: color.withValues(alpha: 0.2), shape: BoxShape.circle, ), child: IconButton( icon: Icon(icon, color: color), onPressed: onPressed, ), ), const SizedBox(height: 4), Text( label, style: TextStyle(fontSize: 12, color: Colors.grey.shade700), ), ], ); } String _formatDuration(Duration duration) { final minutes = duration.inMinutes.toString().padLeft(2, '0'); final seconds = (duration.inSeconds % 60).toString().padLeft(2, '0'); return '$minutes:$seconds'; } void _showExitDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('退出游戏'), content: const Text('确定要退出当前游戏吗?游戏进度将会保存。'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), TextButton( onPressed: () { Navigator.pop(context); // 关闭对话框 Navigator.pop(context); // 返回上一页 }, child: const Text('退出'), ), ], ), ); } void _showRestartDialog(PuzzleProvider provider) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('重新开始'), content: const Text('确定要重新开始游戏吗?当前进度将会丢失。'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), TextButton( onPressed: () { provider.restartGame(); Navigator.pop(context); }, child: const Text('重新开始'), ), ], ), ); } void _showPauseDialog() { showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text('游戏暂停'), content: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.pause_circle, size: 64, color: Colors.purple), const SizedBox(height: 16), const Text('游戏已暂停'), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('继续游戏'), ), ], ), ); } }