diff --git a/android/gradle.properties b/android/gradle.properties
index 4bb5439f682100f8ef4ba80a557fe4f2f0ab14c2..6bf54a6ed821c19f76d860d4a24e7c85d440b575 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.9
-app.versionCode=9
+app.versionName=0.0.10
+app.versionCode=10
diff --git a/fastlane/metadata/android/en-US/changelogs/10.txt b/fastlane/metadata/android/en-US/changelogs/10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f4722ac93441fb3adbabd2e02e515699ad03b527
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/10.txt
@@ -0,0 +1 @@
+Add animation on delete blocks.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/10.txt b/fastlane/metadata/android/fr-FR/changelogs/10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..556780846a9dbb43a76fc2cfd2523a4efe3e09a1
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/10.txt
@@ -0,0 +1 @@
+Ajout d'animation à la suppression des blocs.
diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart
index 86e9c01600c0e8acadd57612553ab4fd813f1a18..e3803dfe9624f7d0de400281fa0fd5b63afdea4b 100644
--- a/lib/cubit/game_cubit.dart
+++ b/lib/cubit/game_cubit.dart
@@ -95,37 +95,35 @@ class GameCubit extends HydratedCubit<GameState> {
     block.forEach((CellLocation blockItemToDelete) {
       this.updateCellValue(blockItemToDelete, null);
     });
-    // Gravity!
-    this.moveCellsDown();
   }
 
   int getScoreFromBlock(int blockSize) {
     return 3 * (blockSize - 2);
   }
 
-  void tapOnCell(CellLocation tappedCellLocation) {
+  List<CellLocation> tapOnCell(CellLocation tappedCellLocation) {
     final Game currentGame = this.state.currentGame;
 
     final int? cellValue = currentGame.getCellValue(tappedCellLocation);
-    print('Tap on cell: col=' +
-        tappedCellLocation.col.toString() +
-        ' ; row=' +
-        tappedCellLocation.row.toString() +
-        ' ; value=' +
-        cellValue.toString());
 
     if (cellValue != null) {
-      List<CellLocation> block = currentGame.getSiblingCells(tappedCellLocation, []);
-      print('block size: ' + block.length.toString());
-      if (block.length >= 3) {
-        this.deleteBlock(block);
+      List<CellLocation> blockCells = currentGame.getSiblingCells(tappedCellLocation, []);
+      if (blockCells.length >= 3) {
+        this.deleteBlock(blockCells);
         this.increaseMovesCount();
-        this.increaseScore(getScoreFromBlock(block.length));
-        this.updateAvailableBlocksCount();
+        this.increaseScore(getScoreFromBlock(blockCells.length));
+        return blockCells;
       }
     }
 
-    if (!currentGame.hasAtLeastOneAvailableBlock()) {
+    return [];
+  }
+
+  void postAnimate() {
+    this.moveCellsDown();
+    this.updateAvailableBlocksCount();
+
+    if (!this.state.currentGame.hasAtLeastOneAvailableBlock()) {
       print('no more block found. finish game.');
       this.updateGameIsFinished(true);
     }
diff --git a/lib/models/cell_location.dart b/lib/models/cell_location.dart
index b7a70ffd791c385899cb582eabcc664bd163ad74..be4c1063325d10d5bce45deb13ec60a1bedbc224 100644
--- a/lib/models/cell_location.dart
+++ b/lib/models/cell_location.dart
@@ -10,4 +10,8 @@ class CellLocation {
   factory CellLocation.go(int row, int col) {
     return new CellLocation(col: col, row: row);
   }
+
+  String toString() {
+    return 'CellLocation(col: ' + col.toString() + ', row: ' + row.toString() + ')';
+  }
 }
diff --git a/lib/models/game.dart b/lib/models/game.dart
index 90426df03f00d80d8082dcdb98f95c868328dd3d..91f636bdb351588ee52f1e17ef1f2e9134a07af3 100644
--- a/lib/models/game.dart
+++ b/lib/models/game.dart
@@ -82,8 +82,8 @@ class Game {
 
     final int? referenceValue = this.getCellValue(referenceCellLocation);
 
-    for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
-      for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
+    for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
+      for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
         if (deltaCol == 0 || deltaRow == 0) {
           final int candidateRow = referenceCellLocation.row + deltaRow;
           final int candidateCol = referenceCellLocation.col + deltaCol;
@@ -94,7 +94,7 @@ class Game {
 
             if (this.getCellValue(candidateLocation) == referenceValue) {
               bool alreadyFound = false;
-              for (var index = 0; index < siblingCells.length; index++) {
+              for (int index = 0; index < siblingCells.length; index++) {
                 if ((siblingCells[index].row == candidateRow) &&
                     (siblingCells[index].col == candidateCol)) {
                   alreadyFound = true;
@@ -119,8 +119,8 @@ class Game {
 
     final List<List<CellLocation>> blocks = [];
 
-    for (var row = 0; row < boardSizeVertical; row++) {
-      for (var col = 0; col < boardSizeHorizontal; col++) {
+    for (int row = 0; row < boardSizeVertical; row++) {
+      for (int col = 0; col < boardSizeHorizontal; col++) {
         final CellLocation cellLocation = CellLocation.go(row, col);
         if (game.getCellValue(cellLocation) != null) {
           // if current cell not already in a found block
@@ -150,8 +150,8 @@ class Game {
     final int boardSizeHorizontal = this.settings.boardSize;
     final int boardSizeVertical = this.settings.boardSize;
 
-    for (var row = 0; row < boardSizeVertical; row++) {
-      for (var col = 0; col < boardSizeHorizontal; col++) {
+    for (int row = 0; row < boardSizeVertical; row++) {
+      for (int col = 0; col < boardSizeHorizontal; col++) {
         final CellLocation cellLocation = CellLocation.go(row, col);
         if (this.getCellValue(cellLocation) != null) {
           final List<CellLocation> block = this.getSiblingCells(cellLocation, []);
diff --git a/lib/models/game_board.dart b/lib/models/game_board.dart
index bea21887bc25242d69c813194674ac15d1b8092e..71776f1ae4171dc8033fb51faa6eca65a537651b 100644
--- a/lib/models/game_board.dart
+++ b/lib/models/game_board.dart
@@ -22,9 +22,9 @@ class GameBoard {
     final rand = new Random();
 
     List<List<GameCell>> cells = [];
-    for (var rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
+    for (int rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
       List<GameCell> row = [];
-      for (var colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
+      for (int colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
         int value = 1 + rand.nextInt(maxValue);
         row.add(GameCell(value));
       }
@@ -45,9 +45,9 @@ class GameBoard {
     print('Board:');
     print(horizontalRule);
 
-    for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
+    for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
       String row = '| ';
-      for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
+      for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
         row += cells[rowIndex][colIndex].value.toString();
       }
       row += ' |';
diff --git a/lib/ui/painters/game_board_painter.dart b/lib/ui/painters/game_board_painter.dart
index 08cad440ef153547368e0ec7ac1a1c5e01513137..5bb11b449d3c403ce63966f8e4b8c1a461b407bf 100644
--- a/lib/ui/painters/game_board_painter.dart
+++ b/lib/ui/painters/game_board_painter.dart
@@ -8,114 +8,68 @@ import 'package:jeweled/models/cell_location.dart';
 import 'package:jeweled/utils/color_theme.dart';
 
 class GameBoardPainter extends CustomPainter {
-  const GameBoardPainter(this.currentGame);
+  const GameBoardPainter({required this.game, required this.animations});
 
-  final Game currentGame;
-
-  static const drawTextValue = false;
+  final Game game;
+  final List<List<Animation<double>?>> animations;
 
   @override
   void paint(Canvas canvas, Size size) {
-    final int sizeHorizontal = currentGame.settings.boardSize;
-    final int sizeVertical = currentGame.settings.boardSize;
-
-    final List cells = currentGame.board.cells;
-    final double cellSize = size.width / max(sizeHorizontal, sizeVertical);
+    final double canvasSize = min(size.width, size.height);
 
-    // background
-    for (var row = 0; row < sizeVertical; row++) {
-      final double y = cellSize * row;
-      for (var col = 0; col < sizeHorizontal; col++) {
-        final double x = cellSize * col;
+    final int cellsCountHorizontal = game.settings.boardSize;
+    final int cellsCountVertical = game.settings.boardSize;
 
-        final GameCell cell = cells[row][col];
-        final int colorCode = ColorTheme.getColorCode(cell.value);
-
-        final cellPaintBackground = Paint();
-        cellPaintBackground.color = Color(colorCode);
-        cellPaintBackground.style = PaintingStyle.fill;
-
-        final Rect cellBackground =
-            Rect.fromPoints(Offset(x - 1, y - 1), Offset(x + cellSize + 2, y + cellSize + 2));
-
-        canvas.drawRect(cellBackground, cellPaintBackground);
-
-        // draw value on cell
-        if (drawTextValue) {
-          final textPainter = TextPainter(
-            text: TextSpan(
-              text: cell.value.toString(),
-              style: const TextStyle(
-                color: Colors.black,
-                fontSize: 15,
-              ),
-            ),
-            textDirection: TextDirection.ltr,
-          )..layout(
-              minWidth: 0,
-              maxWidth: size.width,
-            );
-          textPainter.paint(canvas, Offset(x + 4, y + 2));
-        }
-      }
-    }
+    final double cellSize = canvasSize / max(cellsCountHorizontal, cellsCountVertical);
 
-    // borders
+    const double overlapping = 1;
     const double borderSize = 4;
-    final cellPaintBorder = Paint();
-    cellPaintBorder.color = ColorTheme.getBorderColor();
-    cellPaintBorder.strokeWidth = borderSize;
-    cellPaintBorder.strokeCap = StrokeCap.round;
-
-    for (var row = 0; row < sizeVertical; row++) {
-      final double y = cellSize * row;
-      for (var col = 0; col < sizeHorizontal; col++) {
+
+    // background
+    for (int row = 0; row < cellsCountVertical; row++) {
+      final double yOrigin = cellSize * row;
+      for (int col = 0; col < cellsCountHorizontal; col++) {
         final double x = cellSize * col;
 
-        final GameCell cell = cells[row][col];
-        final int? cellValue = cell.value;
+        final CellLocation cellLocation = CellLocation.go(row, col);
+        final GameCell cell = game.getCell(cellLocation);
+        if (cell.value != null) {
+          final int colorCode = ColorTheme.getColorCode(cell.value);
 
-        // top border
-        if ((row == 0) ||
-            (row > 1 &&
-                cellValue != currentGame.getCellValue(CellLocation.go(row - 1, col)))) {
-          final Offset borderStart = Offset(x, y);
-          final Offset borderStop = Offset(x + cellSize, y);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
+          final Animation<double>? translation = this.animations[row][col];
+          final double y = yOrigin + (translation?.value ?? 0) * cellSize;
 
-        // bottom border
-        if ((row == sizeVertical - 1) ||
-            ((row + 1) < sizeVertical &&
-                cellValue != currentGame.getCellValue(CellLocation.go(row + 1, col)))) {
-          final Offset borderStart = Offset(x, y + cellSize);
-          final Offset borderStop = Offset(x + cellSize, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
+          final cellPaintBackground = Paint();
+          cellPaintBackground.color = Color(colorCode);
+          cellPaintBackground.style = PaintingStyle.fill;
 
-        // left border
-        if ((col == 0) ||
-            (col > 1 &&
-                cellValue != currentGame.getCellValue(CellLocation.go(row, col - 1)))) {
-          final Offset borderStart = Offset(x, y);
-          final Offset borderStop = Offset(x, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
+          final Rect cellBackground = Rect.fromPoints(Offset(x - overlapping, y - overlapping),
+              Offset(x + cellSize + overlapping, y + cellSize + overlapping));
 
-        // right border
-        if ((col == sizeHorizontal - 1) ||
-            ((col + 1) < sizeHorizontal &&
-                cellValue != currentGame.getCellValue(CellLocation.go(row, col + 1)))) {
-          final Offset borderStart = Offset(x + cellSize, y);
-          final Offset borderStop = Offset(x + cellSize, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
+          canvas.drawRect(cellBackground, cellPaintBackground);
         }
       }
     }
+
+    // board borders
+    final boardPaintBorder = Paint();
+    boardPaintBorder.color = ColorTheme.getBorderColor();
+    boardPaintBorder.strokeWidth = borderSize;
+    boardPaintBorder.strokeCap = StrokeCap.round;
+
+    final Offset boardTopLeft = Offset(0, 0);
+    final Offset boardTopRight = Offset(canvasSize, 0);
+    final Offset boardBottomLeft = Offset(0, canvasSize);
+    final Offset boardBottomRight = Offset(canvasSize, canvasSize);
+
+    canvas.drawLine(boardTopLeft, boardTopRight, boardPaintBorder);
+    canvas.drawLine(boardTopRight, boardBottomRight, boardPaintBorder);
+    canvas.drawLine(boardBottomRight, boardBottomLeft, boardPaintBorder);
+    canvas.drawLine(boardBottomLeft, boardTopLeft, boardPaintBorder);
   }
 
   @override
   bool shouldRepaint(CustomPainter oldDelegate) {
-    return false;
+    return true;
   }
 }
diff --git a/lib/ui/widgets/game.dart b/lib/ui/widgets/game.dart
index 3170f63f912fb6b0a8dd03ec3a62dc59fe75a5a9..443c554a062e2b6ccf10e29971d9905e0399689b 100644
--- a/lib/ui/widgets/game.dart
+++ b/lib/ui/widgets/game.dart
@@ -16,17 +16,22 @@ class GameWidget extends StatelessWidget {
       builder: (BuildContext context, GameState gameState) {
         final Game currentGame = gameState.currentGame;
 
-        return Column(
-          mainAxisAlignment: MainAxisAlignment.start,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            const SizedBox(height: 8),
-            const GameTopIndicatorWidget(),
-            const SizedBox(height: 2),
-            const GameBoard(),
-            const SizedBox(height: 2),
-            currentGame.isFinished ? const GameBottomButtonsWidget() : const SizedBox.shrink(),
-          ],
+        return Container(
+          padding: const EdgeInsets.all(4),
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              const SizedBox(height: 8),
+              const GameTopIndicatorWidget(),
+              const SizedBox(height: 2),
+              const GameBoard(),
+              const SizedBox(height: 2),
+              currentGame.isFinished
+                  ? const GameBottomButtonsWidget()
+                  : const SizedBox.shrink(),
+            ],
+          ),
         );
       },
     );
diff --git a/lib/ui/widgets/game_board.dart b/lib/ui/widgets/game_board.dart
index ab9265d89728819612587a86e890975cbc4996f0..746ebbc572867642cb81689939ef30f5c5f2d601 100644
--- a/lib/ui/widgets/game_board.dart
+++ b/lib/ui/widgets/game_board.dart
@@ -6,9 +6,89 @@ 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 StatelessWidget {
+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.settings.boardSize);
+              setState(() {});
+
+              controller.dispose();
+            }
+          });
+
+    // Count translation length for each cell to move
+    final List<List<int>> stepsDownCounts = List.generate(
+      currentGame.settings.boardSize,
+      (i) => List.generate(
+        currentGame.settings.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
+    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);
+      }
+    });
+
+    controller.forward().orCancel;
+  }
+
   @override
   Widget build(BuildContext context) {
     final double displayWidth = MediaQuery.of(context).size.width;
@@ -18,20 +98,27 @@ class GameBoard extends StatelessWidget {
         builder: (BuildContext context, GameState gameState) {
           final Game currentGame = gameState.currentGame;
 
+          if (this.animations.length == 0) {
+            resetAnimations(currentGame.settings.boardSize);
+          }
+
           return GestureDetector(
             onTapUp: (details) {
-              double xTap = details.localPosition.dx;
-              double yTap = details.localPosition.dy;
-              int col = xTap ~/ (displayWidth / currentGame.settings.boardSize);
-              int row = yTap ~/ (displayWidth / currentGame.settings.boardSize);
+              final double xTap = details.localPosition.dx;
+              final double yTap = details.localPosition.dy;
+              final int col = xTap ~/ (displayWidth / currentGame.settings.boardSize);
+              final int row = yTap ~/ (displayWidth / currentGame.settings.boardSize);
 
               final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-              gameCubit.tapOnCell(CellLocation.go(row, col));
+              animateCells(gameCubit.tapOnCell(CellLocation.go(row, col)));
             },
             child: CustomPaint(
               size: Size(displayWidth, displayWidth),
               willChange: false,
-              painter: GameBoardPainter(currentGame),
+              painter: GameBoardPainter(
+                game: currentGame,
+                animations: this.animations,
+              ),
               isComplex: true,
             ),
           );
diff --git a/lib/utils/color_theme.dart b/lib/utils/color_theme.dart
index 2b10565a36d1f956202b2d30f13fb21bef8bd707..18d2006fc56b8a320b4caa921e2577aaf93216e2 100644
--- a/lib/utils/color_theme.dart
+++ b/lib/utils/color_theme.dart
@@ -29,6 +29,6 @@ class ColorTheme {
   }
 
   static Color getBorderColor() {
-    return Colors.black;
+    return Colors.grey;
   }
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 7f45afaa50d390a418c62fb833059908491b3c9a..3dd4fa9f2549c9daba7b0e9ee1ffa32ff7a26660 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Jeweled Game
 
 publish_to: 'none'
 
-version: 0.0.9+9
+version: 0.0.10+10
 
 environment:
   sdk: '^3.0.0'