import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:random/cubit/activity/activity_cubit.dart'; import 'package:random/models/activity/activity.dart'; import 'package:random/models/settings/settings_activity.dart'; import 'package:random/ui/painters/cell_painter.dart'; import 'package:random/ui/widgets/game/game_score.dart'; class GameBoardWidget extends StatefulWidget { const GameBoardWidget({ super.key, required this.activity, required this.widgetSize, }); final Activity activity; final Size widgetSize; @override State<GameBoardWidget> createState() => _GameBoardWidget(); } class _GameBoardWidget extends State<GameBoardWidget> with TickerProviderStateMixin { List<List<Animation<double>?>> animations = []; void resetAnimations(ActivitySettings activitySettings) { final int boardSize = activitySettings.boardSizeValue; animations = List.generate( boardSize, (i) => List.generate( boardSize, (i) => null, ), ); } void removeCell(BuildContext context, int x, int y) { final ActivityCubit activityCubit = BlocProvider.of<ActivityCubit>(context); final Activity updatedGame = activityCubit.state.currentActivity; // "remove" cell, update counters updatedGame.increaseScore(updatedGame.getCellValue(x, y)); updatedGame.increaseMovesCount(); updatedGame.updateCellValue(x, y, null); setState(() {}); // "move down" cells final controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 750), )..addListener(() { if (mounted) { setState(() {}); } }); if (mounted) { setState(() {}); } Animation<double> animation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( curve: Curves.bounceOut, parent: controller, )) ..addStatusListener((status) { if (status == AnimationStatus.completed) { // Update cell values for (var i = 0; i < y; i++) { updatedGame.updateCellValue(x, (y - i), updatedGame.getCellValue(x, (y - i) - 1)); } updatedGame.setRandomCellValue(x, 0, updatedGame.activitySettings); resetAnimations(updatedGame.activitySettings); setState(() {}); controller.dispose(); } }); for (var i = 0; i < y; i++) { animations[(y - i) - 1][x] = animation; } controller.forward().orCancel; } Widget buildBoard() { final double widgetWidth = widget.widgetSize.width; final double widgetHeight = widget.widgetSize.height; final int rowsCount = widget.activity.activitySettings.boardSizeValue; final int columnsCount = widget.activity.activitySettings.boardSizeValue; final double cellWidth = widgetWidth / columnsCount; final double cellHeight = widgetHeight / rowsCount; if (animations.isEmpty) { resetAnimations(widget.activity.activitySettings); } final List<Widget> cells = []; for (var y = 0; y < rowsCount; y++) { for (var x = 0; x < columnsCount; x++) { final int? value = widget.activity.getCellValue(x, y); if (value != null) { final Animation<double>? translation = animations[y][x]; final Widget cellContent = CustomPaint( size: Size(cellWidth, cellHeight), willChange: false, painter: CellPainter(value: value), ); final Widget cellWidget = Positioned( left: (x * cellWidth).toDouble(), top: ((y + (translation?.value ?? 0)) * cellHeight).toDouble(), child: SizedBox( width: cellWidth, height: cellHeight, child: cellContent, ), ); cells.add(cellWidget); } } } return Container( width: widgetWidth, height: widgetHeight, color: Colors.black, child: Stack( children: cells, ), ); } Widget interactiveBoard(BuildContext context) { final double widgetWidth = widget.widgetSize.width; final double widgetHeight = widget.widgetSize.height; final int rowsCount = widget.activity.activitySettings.boardSizeValue; final int columnsCount = widget.activity.activitySettings.boardSizeValue; return GestureDetector( child: buildBoard(), onTapUp: (details) { bool canRemoveCell = true; for (var row in animations) { for (var cell in row) { if (cell != null) { canRemoveCell = false; } } } if (canRemoveCell) { double xTap = details.localPosition.dx; double yTap = details.localPosition.dy; int x = (xTap / widgetWidth * columnsCount).toInt(); int y = (yTap / widgetHeight * rowsCount).toInt(); printlog('[$x,$y]'); removeCell(context, x, y); } else { printlog('animation in progress...'); } }, ); } @override Widget build(BuildContext context) { return Column( children: [ interactiveBoard(context), GameScoreWidget(activity: widget.activity), ], ); } }