Select Git revision
contents.xcworkspacedata
-
Benoît Harrault authoredBenoît Harrault authored
game_board.dart 4.29 KiB
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jeweled/cubit/game_cubit.dart';
import 'package:jeweled/models/game.dart';
import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/ui/painters/game_board_painter.dart';
class GameBoard extends StatefulWidget {
const GameBoard({super.key});
@override
State<GameBoard> createState() => _GameBoard();
}
class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
final int animationDuration = 500;
List<List<Animation<double>?>> animations = [];
void resetAnimations(int boardSize) {
this.animations = List.generate(
boardSize,
(i) => List.generate(
boardSize,
(i) => null,
),
);
}
void animateCells(List<CellLocation> cellsToRemove) {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
final Game currentGame = gameCubit.state.currentGame;
// "move down" cells
final controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: animationDuration),
)..addListener(() {
if (mounted) {
setState(() {});
}
});
if (mounted) {
setState(() {});
}
Animation<double> animation(int count) => Tween(
begin: 0.0,
end: count.toDouble(),
).animate(CurvedAnimation(
curve: Curves.bounceOut,
parent: controller,
))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
gameCubit.postAnimate();
resetAnimations(currentGame.gameSettings.boardSize);
setState(() {});
controller.dispose();
}
});
// Count translation length for each cell to move
final List<List<int>> stepsDownCounts = List.generate(
currentGame.gameSettings.boardSize,
(i) => List.generate(
currentGame.gameSettings.boardSize,
(i) => 0,
),
);
cellsToRemove.forEach((cellToRemove) {
for (int i = 0; i < cellToRemove.row; i++) {
stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col] += 1;
}
});
// Build animation for each cell to move
bool needAnimation = false;
cellsToRemove.forEach((cellToRemove) {
for (int i = 0; i < cellToRemove.row; i++) {
final int stepsCount = stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col];
this.animations[(cellToRemove.row - i) - 1][cellToRemove.col] = animation(stepsCount);
needAnimation = true;
}
});
controller.forward().orCancel;
if (!needAnimation) {
gameCubit.postAnimate();
}
}
@override
Widget build(BuildContext context) {
final double displayWidth = MediaQuery.of(context).size.width;
return Center(
child: BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame;
if (this.animations.length == 0) {
resetAnimations(currentGame.gameSettings.boardSize);
}
return GestureDetector(
onTapUp: (details) {
bool animationInProgress = false;
animations.forEach((row) {
row.forEach((cell) {
if (cell != null) {
animationInProgress = true;
}
});
});
if (!animationInProgress) {
final double xTap = details.localPosition.dx;
final double yTap = details.localPosition.dy;
final int col = xTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
final int row = yTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
animateCells(gameCubit.tapOnCell(CellLocation.go(row, col)));
}
},
child: CustomPaint(
size: Size(displayWidth, displayWidth),
willChange: false,
painter: GameBoardPainter(
game: currentGame,
animations: this.animations,
),
isComplex: true,
),
);
},
),
);
}
}