Skip to content
Snippets Groups Projects
Select Git revision
  • b3d239513514ddf29ab0cd50b1c8c6c2b2aa0e54
  • master default protected
  • 56-upgrade-framework-and-dependencies
  • 39-improve-app-metadata
  • 28-add-high-scores
  • 32-add-block-size-limit-parameter
  • 29-use-real-board-painter-to-draw-parameters-items
  • Release_0.9.0_47 protected
  • Release_0.8.2_46 protected
  • Release_0.8.1_45 protected
  • Release_0.8.0_44 protected
  • Release_0.7.0_43 protected
  • Release_0.6.0_42 protected
  • Release_0.5.0_41 protected
  • Release_0.4.2_40 protected
  • Release_0.4.1_39 protected
  • Release_0.4.0_38 protected
  • Release_0.3.1_37 protected
  • Release_0.3.0_36 protected
  • Release_0.2.1_35 protected
  • Release_0.2.0_34 protected
  • Release_0.1.1_33 protected
  • Release_0.1.0_32 protected
  • Release_0.0.31_31 protected
  • Release_0.0.30_30 protected
  • Release_0.0.29_29 protected
  • Release_0.0.28_28 protected
27 results

game_board.dart

Blame
  • 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,
                ),
              );
            },
          ),
        );
      }
    }