diff --git a/android/gradle.properties b/android/gradle.properties
index 2ee39041593560d6cd2a6b3cb4d73aebcc41f837..9ebd19f8aea61eb37ed1b92717dc56b426e5a1b4 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.1.16
-app.versionCode=37
+app.versionName=0.1.17
+app.versionCode=38
diff --git a/assets/fonts/Nunito-Bold.ttf b/assets/fonts/Nunito-Bold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..6519feb781449ebe0015cbc74dfd9e13110fbba9
Binary files /dev/null and b/assets/fonts/Nunito-Bold.ttf differ
diff --git a/assets/fonts/Nunito-Light.ttf b/assets/fonts/Nunito-Light.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..8a0736c41cd6c2a1225d356bf274de1d0afc3497
Binary files /dev/null and b/assets/fonts/Nunito-Light.ttf differ
diff --git a/assets/fonts/Nunito-Medium.ttf b/assets/fonts/Nunito-Medium.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..88fccdc0638b6f5d6ac49d9d269dc3d518618ad1
Binary files /dev/null and b/assets/fonts/Nunito-Medium.ttf differ
diff --git a/assets/fonts/Nunito-Regular.ttf b/assets/fonts/Nunito-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..e7b8375a896ef0cd8e06730a78c84532b377e784
Binary files /dev/null and b/assets/fonts/Nunito-Regular.ttf differ
diff --git a/assets/translations/en.json b/assets/translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..a598046a8ec88733c979127d5ed13aebf0a0f453
--- /dev/null
+++ b/assets/translations/en.json
@@ -0,0 +1,16 @@
+{
+  "app_name": "Minehunter",
+
+  "long_press_to_quit": "Long press to quit game...",
+  
+  "bottom_nav_home": "Game",
+  "bottom_nav_settings": "Settings",
+  "bottom_nav_about": "About",
+
+  "settings_title": "Settings",
+  "settings_label_theme": "Theme mode",
+
+  "about_title": "About",
+  "about_content": "Minehunter.",
+  "about_version": "Version: {version}"
+}
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..9243b745c43ec3c9f03af8f7127d356dd7a3fa85
--- /dev/null
+++ b/assets/translations/fr.json
@@ -0,0 +1,16 @@
+{
+  "app_name": "Démineur",
+
+  "long_press_to_quit": "Appuyer longtemps pour quitter le jeu...",
+
+  "bottom_nav_home": "Jeu",
+  "bottom_nav_settings": "Réglages",
+  "bottom_nav_about": "Infos",
+
+  "settings_title": "Réglages",
+  "settings_label_theme": "Thème de couleurs",
+
+  "about_title": "Informations",
+  "about_content": "Démineur.",
+  "about_version": "Version : {version}"
+}
diff --git a/fastlane/metadata/android/en-US/changelogs/38.txt b/fastlane/metadata/android/en-US/changelogs/38.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6b884ec4ea3e942b988f5815114a1c3ab59810b8
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/38.txt
@@ -0,0 +1 @@
+Improve game conception/architecture.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/38.txt b/fastlane/metadata/android/fr-FR/changelogs/38.txt
new file mode 100644
index 0000000000000000000000000000000000000000..386b2cdcc090f19264599ba6dc6b215246489bce
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/38.txt
@@ -0,0 +1 @@
+Amélioration de la conception/architecture du jeu.
diff --git a/lib/config/menu.dart b/lib/config/menu.dart
new file mode 100644
index 0000000000000000000000000000000000000000..689ccfa302de7d5d9eaa7eedbd3f62e1e5776989
--- /dev/null
+++ b/lib/config/menu.dart
@@ -0,0 +1,53 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:minehunter/ui/screens/about_page.dart';
+import 'package:minehunter/ui/screens/game_page.dart';
+import 'package:minehunter/ui/screens/settings_page.dart';
+import 'package:unicons/unicons.dart';
+
+class MenuItem {
+  final String code;
+  final Icon icon;
+  final Widget page;
+
+  const MenuItem({
+    required this.code,
+    required this.icon,
+    required this.page,
+  });
+}
+
+class Menu {
+  static List<MenuItem> items = [
+    const MenuItem(
+      code: 'bottom_nav_home',
+      icon: Icon(UniconsLine.home),
+      page: GamePage(),
+    ),
+    const MenuItem(
+      code: 'bottom_nav_settings',
+      icon: Icon(UniconsLine.setting),
+      page: SettingsPage(),
+    ),
+    const MenuItem(
+      code: 'bottom_nav_about',
+      icon: Icon(UniconsLine.info_circle),
+      page: AboutPage(),
+    ),
+  ];
+
+  static Widget getPageWidget(int pageIndex) {
+    return Menu.items.elementAt(pageIndex).page;
+  }
+
+  static List<BottomNavigationBarItem> getMenuItems() {
+    return Menu.items
+        .map((MenuItem item) => BottomNavigationBarItem(
+              icon: item.icon,
+              label: tr(item.code),
+            ))
+        .toList();
+  }
+
+  static int itemsCount = Menu.items.length;
+}
diff --git a/lib/config/theme.dart b/lib/config/theme.dart
new file mode 100644
index 0000000000000000000000000000000000000000..be390348c7868e7c63387df13e13c46de43f8a23
--- /dev/null
+++ b/lib/config/theme.dart
@@ -0,0 +1,196 @@
+import 'package:flutter/material.dart';
+
+/// Colors from Tailwind CSS (v3.0) - June 2022
+///
+/// https://tailwindcss.com/docs/customizing-colors
+
+const int _primaryColor = 0xFF6366F1;
+const MaterialColor primarySwatch = MaterialColor(_primaryColor, <int, Color>{
+  50: Color(0xFFEEF2FF), // indigo-50
+  100: Color(0xFFE0E7FF), // indigo-100
+  200: Color(0xFFC7D2FE), // indigo-200
+  300: Color(0xFFA5B4FC), // indigo-300
+  400: Color(0xFF818CF8), // indigo-400
+  500: Color(_primaryColor), // indigo-500
+  600: Color(0xFF4F46E5), // indigo-600
+  700: Color(0xFF4338CA), // indigo-700
+  800: Color(0xFF3730A3), // indigo-800
+  900: Color(0xFF312E81), // indigo-900
+});
+
+const int _textColor = 0xFF64748B;
+const MaterialColor textSwatch = MaterialColor(_textColor, <int, Color>{
+  50: Color(0xFFF8FAFC), // slate-50
+  100: Color(0xFFF1F5F9), // slate-100
+  200: Color(0xFFE2E8F0), // slate-200
+  300: Color(0xFFCBD5E1), // slate-300
+  400: Color(0xFF94A3B8), // slate-400
+  500: Color(_textColor), // slate-500
+  600: Color(0xFF475569), // slate-600
+  700: Color(0xFF334155), // slate-700
+  800: Color(0xFF1E293B), // slate-800
+  900: Color(0xFF0F172A), // slate-900
+});
+
+const Color errorColor = Color(0xFFDC2626); // red-600
+
+final ColorScheme lightColorScheme = ColorScheme.light(
+  primary: primarySwatch.shade500,
+  secondary: primarySwatch.shade500,
+  onSecondary: Colors.white,
+  error: errorColor,
+  background: textSwatch.shade200,
+  onBackground: textSwatch.shade500,
+  onSurface: textSwatch.shade500,
+  surface: textSwatch.shade50,
+  surfaceVariant: Colors.white,
+  shadow: textSwatch.shade900.withOpacity(.1),
+);
+
+final ColorScheme darkColorScheme = ColorScheme.dark(
+  primary: primarySwatch.shade500,
+  secondary: primarySwatch.shade500,
+  onSecondary: Colors.white,
+  error: errorColor,
+  background: const Color(0xFF171724),
+  onBackground: textSwatch.shade400,
+  onSurface: textSwatch.shade300,
+  surface: const Color(0xFF262630),
+  surfaceVariant: const Color(0xFF282832),
+  shadow: textSwatch.shade900.withOpacity(.2),
+);
+
+final ThemeData lightTheme = ThemeData(
+  colorScheme: lightColorScheme,
+  fontFamily: 'Nunito',
+  textTheme: TextTheme(
+    displayLarge: TextStyle(
+      color: textSwatch.shade700,
+      fontFamily: 'Nunito',
+    ),
+    displayMedium: TextStyle(
+      color: textSwatch.shade600,
+      fontFamily: 'Nunito',
+    ),
+    displaySmall: TextStyle(
+      color: textSwatch.shade500,
+      fontFamily: 'Nunito',
+    ),
+    headlineLarge: TextStyle(
+      color: textSwatch.shade700,
+      fontFamily: 'Nunito',
+    ),
+    headlineMedium: TextStyle(
+      color: textSwatch.shade600,
+      fontFamily: 'Nunito',
+    ),
+    headlineSmall: TextStyle(
+      color: textSwatch.shade500,
+      fontFamily: 'Nunito',
+    ),
+    titleLarge: TextStyle(
+      color: textSwatch.shade700,
+      fontFamily: 'Nunito',
+    ),
+    titleMedium: TextStyle(
+      color: textSwatch.shade600,
+      fontFamily: 'Nunito',
+    ),
+    titleSmall: TextStyle(
+      color: textSwatch.shade500,
+      fontFamily: 'Nunito',
+    ),
+    bodyLarge: TextStyle(
+      color: textSwatch.shade700,
+      fontFamily: 'Nunito',
+    ),
+    bodyMedium: TextStyle(
+      color: textSwatch.shade600,
+      fontFamily: 'Nunito',
+    ),
+    bodySmall: TextStyle(
+      color: textSwatch.shade500,
+      fontFamily: 'Nunito',
+    ),
+    labelLarge: TextStyle(
+      color: textSwatch.shade700,
+      fontFamily: 'Nunito',
+    ),
+    labelMedium: TextStyle(
+      color: textSwatch.shade600,
+      fontFamily: 'Nunito',
+    ),
+    labelSmall: TextStyle(
+      color: textSwatch.shade500,
+      fontFamily: 'Nunito',
+    ),
+  ),
+);
+
+final ThemeData darkTheme = lightTheme.copyWith(
+  colorScheme: darkColorScheme,
+  textTheme: TextTheme(
+    displayLarge: TextStyle(
+      color: textSwatch.shade200,
+      fontFamily: 'Nunito',
+    ),
+    displayMedium: TextStyle(
+      color: textSwatch.shade300,
+      fontFamily: 'Nunito',
+    ),
+    displaySmall: TextStyle(
+      color: textSwatch.shade400,
+      fontFamily: 'Nunito',
+    ),
+    headlineLarge: TextStyle(
+      color: textSwatch.shade200,
+      fontFamily: 'Nunito',
+    ),
+    headlineMedium: TextStyle(
+      color: textSwatch.shade300,
+      fontFamily: 'Nunito',
+    ),
+    headlineSmall: TextStyle(
+      color: textSwatch.shade400,
+      fontFamily: 'Nunito',
+    ),
+    titleLarge: TextStyle(
+      color: textSwatch.shade200,
+      fontFamily: 'Nunito',
+    ),
+    titleMedium: TextStyle(
+      color: textSwatch.shade300,
+      fontFamily: 'Nunito',
+    ),
+    titleSmall: TextStyle(
+      color: textSwatch.shade400,
+      fontFamily: 'Nunito',
+    ),
+    bodyLarge: TextStyle(
+      color: textSwatch.shade200,
+      fontFamily: 'Nunito',
+    ),
+    bodyMedium: TextStyle(
+      color: textSwatch.shade300,
+      fontFamily: 'Nunito',
+    ),
+    bodySmall: TextStyle(
+      color: textSwatch.shade400,
+      fontFamily: 'Nunito',
+    ),
+    labelLarge: TextStyle(
+      color: textSwatch.shade200,
+      fontFamily: 'Nunito',
+    ),
+    labelMedium: TextStyle(
+      color: textSwatch.shade300,
+      fontFamily: 'Nunito',
+    ),
+    labelSmall: TextStyle(
+      color: textSwatch.shade400,
+      fontFamily: 'Nunito',
+    ),
+  ),
+);
+
+final ThemeData appTheme = darkTheme;
diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..54261399606dd4f345ef4c753fedef10fc2b1f18
--- /dev/null
+++ b/lib/cubit/bottom_nav_cubit.dart
@@ -0,0 +1,30 @@
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+import 'package:minehunter/config/menu.dart';
+
+class BottomNavCubit extends HydratedCubit<int> {
+  BottomNavCubit() : super(0);
+
+  void updateIndex(int index) {
+    if (isIndexAllowed(index)) {
+      emit(index);
+    } else {
+      goToHomePage();
+    }
+  }
+
+  bool isIndexAllowed(int index) {
+    return (index >= 0) && (index < Menu.itemsCount);
+  }
+
+  void goToHomePage() => emit(0);
+
+  @override
+  int fromJson(Map<String, dynamic> json) {
+    return 0;
+  }
+
+  @override
+  Map<String, dynamic>? toJson(int state) {
+    return <String, int>{'pageIndex': state};
+  }
+}
diff --git a/lib/cubit/theme_cubit.dart b/lib/cubit/theme_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b793e895dbb0c672d451cd403e0036c3d9ac9b42
--- /dev/null
+++ b/lib/cubit/theme_cubit.dart
@@ -0,0 +1,31 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+part 'theme_state.dart';
+
+class ThemeCubit extends HydratedCubit<ThemeModeState> {
+  ThemeCubit() : super(const ThemeModeState());
+
+  void getTheme(ThemeModeState state) {
+    emit(state);
+  }
+
+  @override
+  ThemeModeState? fromJson(Map<String, dynamic> json) {
+    switch (json['themeMode']) {
+      case 'ThemeMode.dark':
+        return const ThemeModeState(themeMode: ThemeMode.dark);
+      case 'ThemeMode.light':
+        return const ThemeModeState(themeMode: ThemeMode.light);
+      case 'ThemeMode.system':
+      default:
+        return const ThemeModeState(themeMode: ThemeMode.system);
+    }
+  }
+
+  @override
+  Map<String, String>? toJson(ThemeModeState state) {
+    return <String, String>{'themeMode': state.themeMode.toString()};
+  }
+}
diff --git a/lib/cubit/theme_state.dart b/lib/cubit/theme_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e479a50f12fe72a35a1fd1722ff72afbb692a136
--- /dev/null
+++ b/lib/cubit/theme_state.dart
@@ -0,0 +1,15 @@
+part of 'theme_cubit.dart';
+
+@immutable
+class ThemeModeState extends Equatable {
+  const ThemeModeState({
+    this.themeMode,
+  });
+
+  final ThemeMode? themeMode;
+
+  @override
+  List<Object?> get props => <Object?>[
+        themeMode,
+      ];
+}
diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart
index 3185ed5910a6d3b94398c0577e653a6dcd737ad2..ff05ad891d83af959e8afd498a0d5457eabf4a8a 100644
--- a/lib/entities/cell.dart
+++ b/lib/entities/cell.dart
@@ -1,5 +1,4 @@
 import 'package:flutter/material.dart';
-
 import 'package:minehunter/provider/data.dart';
 import 'package:minehunter/utils/board_animate.dart';
 import 'package:minehunter/utils/board_utils.dart';
@@ -43,7 +42,7 @@ class Cell {
         onTap: () {
           // Set mines on board after first player try
           if (!myProvider.isBoardMined) {
-            myProvider.updateCells(BoardUtils.createBoard(myProvider, row, col));
+            myProvider.updateBoard(BoardUtils.createBoard(myProvider, row, col));
             myProvider.updateIsBoardMined(true);
           }
 
diff --git a/lib/layout/board.dart b/lib/layout/board.dart
deleted file mode 100644
index 857a84f9e8b107d12df5f3bfb6b3501bc6ac2e1b..0000000000000000000000000000000000000000
--- a/lib/layout/board.dart
+++ /dev/null
@@ -1,111 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:minehunter/entities/cell.dart';
-import 'package:minehunter/provider/data.dart';
-
-class Board {
-  static Container buildGameBoard(Data myProvider) {
-    return Container(
-      margin: const EdgeInsets.all(2),
-      padding: const EdgeInsets.all(2),
-      child: Column(
-        children: [
-          buildGameTileset(myProvider),
-        ],
-      ),
-    );
-  }
-
-  static Widget buildGameTileset(Data myProvider) {
-    List<List<Cell>> cells = myProvider.cells;
-    Color borderColor = myProvider.reportMode ? Colors.blue : Colors.black;
-
-    return Container(
-      decoration: BoxDecoration(
-        color: borderColor,
-        borderRadius: BorderRadius.circular(2),
-        border: Border.all(
-          color: borderColor,
-          width: 2,
-        ),
-      ),
-      child: Table(
-        defaultColumnWidth: const IntrinsicColumnWidth(),
-        children: [
-          for (int row = 0; row < myProvider.sizeVertical; row++)
-            TableRow(
-              children: [
-                for (int col = 0; col < myProvider.sizeHorizontal; col++)
-                  Column(
-                    children: [
-                      cells[row][col].widget(myProvider, row, col),
-                    ],
-                  ),
-              ],
-            ),
-        ],
-      ),
-    );
-  }
-
-  static TextButton buildWalkIndicator(Data myProvider) {
-    String reportModeSuffix = myProvider.reportMode ? 'off' : 'on';
-
-    return TextButton(
-      child: Image(
-        image: AssetImage(
-            'assets/skins/${myProvider.parameterSkin}_indicator_walk_$reportModeSuffix.png'),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => myProvider.updateReportMode(false),
-    );
-  }
-
-  static TextButton buildReportIndicator(Data myProvider) {
-    String reportModeSuffix = myProvider.reportMode ? 'on' : 'off';
-
-    return TextButton(
-      child: Image(
-        image: AssetImage(
-            'assets/skins/${myProvider.parameterSkin}_indicator_report_$reportModeSuffix.png'),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => myProvider.updateReportMode(true),
-    );
-  }
-
-  static TextButton buildToggleFlagModeButton(Data myProvider) {
-    String reportModeSuffix = myProvider.reportMode ? 'on' : 'off';
-
-    return TextButton(
-      child: Image(
-        image: AssetImage(
-            'assets/skins/${myProvider.parameterSkin}_button_mark_mine_$reportModeSuffix.png'),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => myProvider.updateReportMode(!myProvider.reportMode),
-    );
-  }
-
-  static Widget buildToggleFlagModeLayout(Data myProvider) {
-    Image paddingBlock = Image(
-      image: AssetImage('assets/skins/${myProvider.parameterSkin}_empty.png'),
-      fit: BoxFit.fill,
-    );
-
-    return Table(
-      defaultColumnWidth: const IntrinsicColumnWidth(),
-      children: [
-        TableRow(
-          children: [
-            TableCell(child: paddingBlock),
-            TableCell(child: buildWalkIndicator(myProvider)),
-            TableCell(child: buildToggleFlagModeButton(myProvider)),
-            TableCell(child: buildReportIndicator(myProvider)),
-            TableCell(child: paddingBlock),
-          ],
-        ),
-      ],
-    );
-  }
-}
diff --git a/lib/layout/game.dart b/lib/layout/game.dart
deleted file mode 100644
index 28cc6992a22131e8cac0906b9ae485cf78ffe640..0000000000000000000000000000000000000000
--- a/lib/layout/game.dart
+++ /dev/null
@@ -1,133 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:minehunter/layout/board.dart';
-import 'package:minehunter/provider/data.dart';
-import 'package:minehunter/utils/board_utils.dart';
-import 'package:minehunter/utils/game_utils.dart';
-
-class Game {
-  static Widget buildGameWidget(Data myProvider) {
-    bool gameIsFinished = myProvider.gameWin || myProvider.gameFail;
-
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.start,
-      crossAxisAlignment: CrossAxisAlignment.center,
-      children: [
-        const SizedBox(height: 8),
-        Game.buildTopIndicatorWidget(myProvider),
-        const SizedBox(height: 2),
-        Expanded(
-          child: Board.buildGameBoard(myProvider),
-        ),
-        const SizedBox(height: 2),
-        Container(
-          child: gameIsFinished
-              ? Game.buildEndGameMessage(myProvider)
-              : Board.buildToggleFlagModeLayout(myProvider),
-        ),
-        const SizedBox(height: 8),
-      ],
-    );
-  }
-
-  static Widget buildTopIndicatorWidget(Data myProvider) {
-    int flaggedCellsCount = BoardUtils.countFlaggedCells(myProvider.cells);
-    int minesCount = myProvider.minesCount;
-    double blockSize = 40;
-
-    Image flagIconBlock = Image(
-      image: AssetImage('assets/skins/${myProvider.parameterSkin}_tile_flag.png'),
-      fit: BoxFit.fill,
-      height: blockSize,
-      width: blockSize,
-    );
-    Image mineIconBlock = Image(
-      image: AssetImage('assets/skins/${myProvider.parameterSkin}_tile_mine.png'),
-      fit: BoxFit.fill,
-      height: blockSize,
-      width: blockSize,
-    );
-    Text markedMinesCountBlock = Text(
-      flaggedCellsCount.toString(),
-      style: TextStyle(
-        fontSize: blockSize,
-        fontWeight: FontWeight.bold,
-        color: Colors.black,
-      ),
-    );
-    Text placedMinesCountBlock = Text(
-      minesCount.toString(),
-      style: TextStyle(
-        fontSize: blockSize,
-        fontWeight: FontWeight.bold,
-        color: Colors.black,
-      ),
-    );
-
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.center,
-      crossAxisAlignment: CrossAxisAlignment.center,
-      children: [
-        Row(
-          mainAxisAlignment: MainAxisAlignment.center,
-          crossAxisAlignment: CrossAxisAlignment.center,
-          children: [
-            markedMinesCountBlock,
-            flagIconBlock,
-            SizedBox(width: blockSize * 2),
-            mineIconBlock,
-            placedMinesCountBlock,
-          ],
-        )
-      ],
-    );
-  }
-
-  static TextButton buildRestartGameButton(Data myProvider) {
-    return TextButton(
-      child: const Image(
-        image: AssetImage('assets/icons/button_back.png'),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => GameUtils.quitGame(myProvider),
-    );
-  }
-
-  static Widget buildEndGameMessage(Data myProvider) {
-    Image decorationImage = Image(
-      image: AssetImage(myProvider.gameWin
-          ? 'assets/icons/game_win.png'
-          : myProvider.gameFail
-              ? 'assets/icons/game_fail.png'
-              : ''),
-      fit: BoxFit.fill,
-    );
-
-    return Container(
-      margin: const EdgeInsets.all(2),
-      padding: const EdgeInsets.all(2),
-      child: Table(
-        defaultColumnWidth: const IntrinsicColumnWidth(),
-        children: [
-          TableRow(
-            children: [
-              Column(
-                children: [decorationImage],
-              ),
-              Column(
-                children: [
-                  myProvider.animationInProgress
-                      ? decorationImage
-                      : buildRestartGameButton(myProvider)
-                ],
-              ),
-              Column(
-                children: [decorationImage],
-              ),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart
deleted file mode 100644
index 5eef4e86923733fcdb9a25d93862b65174c20335..0000000000000000000000000000000000000000
--- a/lib/layout/parameters.dart
+++ /dev/null
@@ -1,183 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:minehunter/provider/data.dart';
-import 'package:minehunter/utils/game_utils.dart';
-
-class Parameters {
-  static double separatorHeight = 2.0;
-  static double blockMargin = 3.0;
-  static double blockPadding = 2.0;
-  static Color buttonBackgroundColor = Colors.white;
-  static Color buttonBorderColorActive = Colors.blue;
-  static Color buttonBorderColorInactive = Colors.white;
-  static double buttonBorderWidth = 10.0;
-  static double buttonBorderRadius = 8.0;
-  static double buttonPadding = 0.0;
-  static double buttonMargin = 0.0;
-
-  static Widget buildParametersSelector(Data myProvider) {
-    List<Widget> lines = [];
-
-    List<String> parameters = myProvider.availableParameters;
-    for (int index = 0; index < parameters.length; index++) {
-      lines.add(buildParameterSelector(myProvider, parameters[index]));
-      lines.add(SizedBox(height: separatorHeight));
-    }
-
-    myProvider.loadCurrentSavedState();
-    Widget buttonsBlock = myProvider.hasCurrentSavedState()
-        ? buildResumeGameButton(myProvider)
-        : buildStartNewGameButton(myProvider);
-
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.start,
-      crossAxisAlignment: CrossAxisAlignment.center,
-      children: [
-        SizedBox(height: separatorHeight),
-        Expanded(
-          child: Column(
-            mainAxisSize: MainAxisSize.min,
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: lines,
-          ),
-        ),
-        SizedBox(height: separatorHeight),
-        Container(
-          child: buttonsBlock,
-        ),
-      ],
-    );
-  }
-
-  static Image buildImageWidget(String imageAssetCode) {
-    return Image(
-      image: AssetImage('assets/icons/$imageAssetCode.png'),
-      fit: BoxFit.fill,
-    );
-  }
-
-  static Widget buildImageContainerWidget(String imageAssetCode) {
-    return Container(
-      child: buildImageWidget(imageAssetCode),
-    );
-  }
-
-  static Column buildDecorationImageWidget() {
-    return Column(
-      children: [
-        TextButton(
-          child: buildImageContainerWidget('placeholder'),
-          onPressed: () {},
-        ),
-      ],
-    );
-  }
-
-  static Widget buildStartNewGameButton(Data myProvider) {
-    return Container(
-      margin: EdgeInsets.all(blockMargin),
-      padding: EdgeInsets.all(blockPadding),
-      child: Table(
-        defaultColumnWidth: const IntrinsicColumnWidth(),
-        children: [
-          TableRow(
-            children: [
-              buildDecorationImageWidget(),
-              Column(
-                children: [
-                  TextButton(
-                    child: buildImageContainerWidget('button_start'),
-                    onPressed: () => GameUtils.startNewGame(myProvider),
-                  ),
-                ],
-              ),
-              buildDecorationImageWidget(),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-
-  static Widget buildResumeGameButton(Data myProvider) {
-    return Container(
-      margin: EdgeInsets.all(blockMargin),
-      padding: EdgeInsets.all(blockPadding),
-      child: Table(
-        defaultColumnWidth: const IntrinsicColumnWidth(),
-        children: [
-          TableRow(
-            children: [
-              Column(
-                children: [
-                  TextButton(
-                    child: buildImageContainerWidget('button_delete_saved_game'),
-                    onPressed: () => GameUtils.deleteSavedGame(myProvider),
-                  ),
-                ],
-              ),
-              Column(
-                children: [
-                  TextButton(
-                    child: buildImageContainerWidget('button_resume_game'),
-                    onPressed: () => GameUtils.resumeSavedGame(myProvider),
-                  ),
-                ],
-              ),
-              buildDecorationImageWidget(),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-
-  static Widget buildParameterSelector(Data myProvider, String parameterCode) {
-    List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode);
-
-    if (availableValues.length == 1) {
-      return const SizedBox(height: 0.0);
-    }
-
-    return Table(
-      defaultColumnWidth: const IntrinsicColumnWidth(),
-      children: [
-        TableRow(
-          children: [
-            for (int index = 0; index < availableValues.length; index++)
-              Column(
-                children: [
-                  _buildParameterButton(myProvider, parameterCode, availableValues[index])
-                ],
-              ),
-          ],
-        ),
-      ],
-    );
-  }
-
-  static Widget _buildParameterButton(
-      Data myProvider, String parameterCode, String parameterValue) {
-    String currentValue = myProvider.getParameterValue(parameterCode).toString();
-
-    bool isActive = (parameterValue == currentValue);
-    String imageAsset = '${parameterCode}_$parameterValue';
-
-    return TextButton(
-      child: Container(
-        margin: EdgeInsets.all(buttonMargin),
-        padding: EdgeInsets.all(buttonPadding),
-        decoration: BoxDecoration(
-          color: buttonBackgroundColor,
-          borderRadius: BorderRadius.circular(buttonBorderRadius),
-          border: Border.all(
-            color: isActive ? buttonBorderColorActive : buttonBorderColorInactive,
-            width: buttonBorderWidth,
-          ),
-        ),
-        child: buildImageWidget(imageAsset),
-      ),
-      onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
-    );
-  }
-}
diff --git a/lib/main.dart b/lib/main.dart
index b2ecccb6d0cc7b0c09c9b602e7dfebddff56d48b..15ee02989e0c11b9604421e91764241a28edf641 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,15 +1,42 @@
+import 'dart:io';
+
+import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:provider/provider.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hive/hive.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
 import 'package:overlay_support/overlay_support.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:provider/provider.dart';
 
+import 'package:minehunter/config/theme.dart';
+import 'package:minehunter/cubit/bottom_nav_cubit.dart';
+import 'package:minehunter/cubit/theme_cubit.dart';
 import 'package:minehunter/provider/data.dart';
-import 'package:minehunter/screens/home.dart';
+import 'package:minehunter/ui/skeleton.dart';
 
-void main() {
+void main() async {
+  /// Initialize packages
   WidgetsFlutterBinding.ensureInitialized();
-  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
-      .then((value) => runApp(const MyApp()));
+  await EasyLocalization.ensureInitialized();
+  final Directory tmpDir = await getTemporaryDirectory();
+  Hive.init(tmpDir.toString());
+  HydratedBloc.storage = await HydratedStorage.build(
+    storageDirectory: tmpDir,
+  );
+
+  runApp(
+    EasyLocalization(
+      path: 'assets/translations',
+      supportedLocales: const <Locale>[
+        Locale('en'),
+        Locale('fr'),
+      ],
+      fallbackLocale: const Locale('en'),
+      useFallbackTranslations: true,
+      child: const MyApp(),
+    ),
+  );
 }
 
 class MyApp extends StatelessWidget {
@@ -17,19 +44,34 @@ class MyApp extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return ChangeNotifierProvider(
-      create: (BuildContext context) => Data(),
-      child: Consumer<Data>(builder: (context, data, child) {
-        return OverlaySupport(
-          child: MaterialApp(
-            debugShowCheckedModeBanner: false,
-            theme: ThemeData(
-              primaryColor: Colors.blue,
-              visualDensity: VisualDensity.adaptivePlatformDensity,
-            ),
-            home: const Home(),
-            routes: {
-              Home.id: (context) => const Home(),
+    return MultiBlocProvider(
+      providers: [
+        BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
+        BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
+      ],
+      child: BlocBuilder<ThemeCubit, ThemeModeState>(
+          builder: (BuildContext context, ThemeModeState state) {
+        return ChangeNotifierProvider(
+          create: (BuildContext context) => Data(),
+          child: Consumer<Data>(
+            builder: (context, data, child) {
+              return OverlaySupport(
+                child: MaterialApp(
+                  title: 'Minehunter',
+                  home: const SkeletonScreen(),
+
+                  // Theme stuff
+                  theme: lightTheme,
+                  darkTheme: darkTheme,
+                  themeMode: state.themeMode,
+
+                  // Localization stuff
+                  localizationsDelegates: context.localizationDelegates,
+                  supportedLocales: context.supportedLocales,
+                  locale: context.locale,
+                  debugShowCheckedModeBanner: false,
+                ),
+              );
             },
           ),
         );
diff --git a/lib/provider/data.dart b/lib/provider/data.dart
index f1a385a5902ef22e832e63dc269aee25af344ad7..c37658a4d8ada37c3d0d216ed3aeb8cd32ca7f16 100644
--- a/lib/provider/data.dart
+++ b/lib/provider/data.dart
@@ -4,6 +4,10 @@ import 'package:flutter/foundation.dart';
 import 'package:minehunter/entities/cell.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
+typedef Board = List<List<Cell>>;
+typedef AnimatedBoard = List<List<bool>>;
+typedef AnimatedBoardSequence = List<AnimatedBoard>;
+
 class Data extends ChangeNotifier {
   // Configuration available parameters
   final List<String> _availableParameters = ['level', 'size', 'skin'];
@@ -37,7 +41,7 @@ class Data extends ChangeNotifier {
   bool _animationInProgress = false;
   int _sizeVertical = 0;
   int _sizeHorizontal = 0;
-  List<List<Cell>> _cells = [];
+  Board _board = [];
   bool _isBoardMined = false;
   int _minesCount = 0;
   bool _reportMode = false;
@@ -136,13 +140,13 @@ class Data extends ChangeNotifier {
 
   String computeCurrentGameState() {
     String cellsValues = '';
-    for (int rowIndex = 0; rowIndex < _cells.length; rowIndex++) {
-      for (int colIndex = 0; colIndex < _cells[rowIndex].length; colIndex++) {
-        cellsValues += _cells[rowIndex][colIndex].isMined ? 'X' : ' ';
-        cellsValues += _cells[rowIndex][colIndex].isExplored ? 'E' : ' ';
-        cellsValues += _cells[rowIndex][colIndex].isMarked ? 'P' : ' ';
-        cellsValues += _cells[rowIndex][colIndex].isExploded ? '*' : ' ';
-        cellsValues += _cells[rowIndex][colIndex].minesCountAround.toString();
+    for (int rowIndex = 0; rowIndex < _board.length; rowIndex++) {
+      for (int colIndex = 0; colIndex < _board[rowIndex].length; colIndex++) {
+        cellsValues += _board[rowIndex][colIndex].isMined ? 'X' : ' ';
+        cellsValues += _board[rowIndex][colIndex].isExplored ? 'E' : ' ';
+        cellsValues += _board[rowIndex][colIndex].isMarked ? 'P' : ' ';
+        cellsValues += _board[rowIndex][colIndex].isExploded ? '*' : ' ';
+        cellsValues += _board[rowIndex][colIndex].minesCountAround.toString();
         cellsValues += ';';
       }
     }
@@ -216,17 +220,17 @@ class Data extends ChangeNotifier {
     notifyListeners();
   }
 
-  List<List<Cell>> get cells => _cells;
-  void updateCells(List<List<Cell>> cells) {
-    _cells = cells;
+  Board get board => _board;
+  void updateBoard(Board board) {
+    _board = board;
     notifyListeners();
   }
 
   void setCellAsExplored(int row, int col) {
-    _cells[row][col].isExplored = true;
-    _cells[row][col].isMarked = false;
-    if (_cells[row][col].isMined) {
-      _cells[row][col].isExploded = true;
+    _board[row][col].isExplored = true;
+    _board[row][col].isMarked = false;
+    if (_board[row][col].isMined) {
+      _board[row][col].isExploded = true;
     }
 
     saveCurrentGameState();
@@ -234,7 +238,7 @@ class Data extends ChangeNotifier {
   }
 
   void toggleCellMark(int row, int col) {
-    _cells[row][col].isMarked = !_cells[row][col].isMarked;
+    _board[row][col].isMarked = !_board[row][col].isMarked;
 
     saveCurrentGameState();
     notifyListeners();
@@ -271,7 +275,7 @@ class Data extends ChangeNotifier {
   void setAnimatedBackground(List animatedCellsPattern) {
     for (int row = 0; row < _sizeVertical; row++) {
       for (int col = 0; col < _sizeHorizontal; col++) {
-        _cells[row][col].isAnimated = animatedCellsPattern[row][col];
+        _board[row][col].isAnimated = animatedCellsPattern[row][col];
       }
     }
     notifyListeners();
@@ -280,7 +284,7 @@ class Data extends ChangeNotifier {
   void resetAnimatedBackground() {
     for (int row = 0; row < _sizeVertical; row++) {
       for (int col = 0; col < _sizeHorizontal; col++) {
-        _cells[row][col].isAnimated = false;
+        _board[row][col].isAnimated = false;
       }
     }
   }
diff --git a/lib/ui/layout/game.dart b/lib/ui/layout/game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..36796492ca10e14dc7f8d78aa8251118ec275ced
--- /dev/null
+++ b/lib/ui/layout/game.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/layout/tileset.dart';
+import 'package:minehunter/ui/widgets/game/indicator_top.dart';
+import 'package:minehunter/ui/widgets/game/message_game_end.dart';
+import 'package:minehunter/ui/widgets/game/mode_toggle.dart';
+
+class Game extends StatelessWidget {
+  const Game({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final bool gameIsFinished = myProvider.gameWin || myProvider.gameFail;
+
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        const SizedBox(height: 8),
+        TopIndicator(myProvider: myProvider),
+        const SizedBox(height: 2),
+        Expanded(
+          child: Tileset(myProvider: myProvider),
+        ),
+        const SizedBox(height: 2),
+        Container(
+          child: gameIsFinished
+              ? EndGameMessage(myProvider: myProvider)
+              : ToggleGameMode(myProvider: myProvider),
+        ),
+        const SizedBox(height: 8),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/layout/parameters.dart b/lib/ui/layout/parameters.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ee2502a4129b96df781a6d66b1156d56ee17f9a3
--- /dev/null
+++ b/lib/ui/layout/parameters.dart
@@ -0,0 +1,129 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/widgets/home/button_game_resume.dart';
+import 'package:minehunter/ui/widgets/home/button_game_start_new.dart';
+
+class Parameters extends StatelessWidget {
+  const Parameters({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  static const double separatorHeight = 2.0;
+  static const double blockMargin = 3.0;
+  static const double blockPadding = 2.0;
+  static const Color buttonBackgroundColor = Colors.white;
+  static const Color buttonBorderColorActive = Colors.blue;
+  static const Color buttonBorderColorInactive = Colors.white;
+  static const double buttonBorderWidth = 10.0;
+  static const double buttonBorderRadius = 8.0;
+  static const double buttonPadding = 0.0;
+  static const double buttonMargin = 0.0;
+
+  @override
+  Widget build(BuildContext context) {
+    List<Widget> lines = [];
+
+    final List<String> parameters = myProvider.availableParameters;
+    for (int index = 0; index < parameters.length; index++) {
+      lines.add(buildParameterSelector(myProvider, parameters[index]));
+      lines.add(const SizedBox(height: separatorHeight));
+    }
+
+    myProvider.loadCurrentSavedState();
+    Widget buttonsBlock = myProvider.hasCurrentSavedState()
+        ? ResumeGameButton(myProvider: myProvider)
+        : StartNewGameButton(myProvider: myProvider);
+
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        const SizedBox(height: separatorHeight),
+        Expanded(
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: lines,
+          ),
+        ),
+        const SizedBox(height: separatorHeight),
+        Container(
+          child: buttonsBlock,
+        ),
+      ],
+    );
+  }
+
+  static Image buildImageWidget(String imageAssetCode) {
+    return Image(
+      image: AssetImage('assets/icons/$imageAssetCode.png'),
+      fit: BoxFit.fill,
+    );
+  }
+
+  static Container buildImageContainerWidget(String imageAssetCode) {
+    return Container(
+      child: buildImageWidget(imageAssetCode),
+    );
+  }
+
+  static Column buildDecorationImageWidget() {
+    return Column(
+      children: [
+        TextButton(
+          child: buildImageContainerWidget('placeholder'),
+          onPressed: () {},
+        ),
+      ],
+    );
+  }
+
+  Widget buildParameterSelector(Data myProvider, String parameterCode) {
+    final List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode);
+
+    if (availableValues.length == 1) {
+      return const SizedBox(height: 0.0);
+    }
+
+    return Table(
+      defaultColumnWidth: const IntrinsicColumnWidth(),
+      children: [
+        TableRow(
+          children: [
+            for (int index = 0; index < availableValues.length; index++)
+              Column(
+                children: [
+                  buildParameterButton(myProvider, parameterCode, availableValues[index])
+                ],
+              ),
+          ],
+        ),
+      ],
+    );
+  }
+
+  Widget buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
+    final String currentValue = myProvider.getParameterValue(parameterCode).toString();
+
+    final bool isActive = (parameterValue == currentValue);
+    final String imageAsset = '${parameterCode}_$parameterValue';
+
+    return TextButton(
+      child: Container(
+        margin: const EdgeInsets.all(buttonMargin),
+        padding: const EdgeInsets.all(buttonPadding),
+        decoration: BoxDecoration(
+          color: buttonBackgroundColor,
+          borderRadius: BorderRadius.circular(buttonBorderRadius),
+          border: Border.all(
+            color: isActive ? buttonBorderColorActive : buttonBorderColorInactive,
+            width: buttonBorderWidth,
+          ),
+        ),
+        child: buildImageWidget(imageAsset),
+      ),
+      onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
+    );
+  }
+}
diff --git a/lib/ui/layout/tileset.dart b/lib/ui/layout/tileset.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f5986aab892ea72467bd80636da3ee85c1cf600f
--- /dev/null
+++ b/lib/ui/layout/tileset.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+
+class Tileset extends StatelessWidget {
+  const Tileset({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final Board board = myProvider.board;
+    final Color borderColor = myProvider.reportMode ? Colors.blue : Colors.black;
+
+    return Container(
+      margin: const EdgeInsets.all(2),
+      padding: const EdgeInsets.all(2),
+      child: Column(
+        children: [
+          Container(
+            decoration: BoxDecoration(
+              color: borderColor,
+              borderRadius: BorderRadius.circular(2),
+              border: Border.all(
+                color: borderColor,
+                width: 2,
+              ),
+            ),
+            child: Table(
+              defaultColumnWidth: const IntrinsicColumnWidth(),
+              children: [
+                for (int row = 0; row < myProvider.sizeVertical; row++)
+                  TableRow(
+                    children: [
+                      for (int col = 0; col < myProvider.sizeHorizontal; col++)
+                        Column(
+                          children: [
+                            board[row][col].widget(myProvider, row, col),
+                          ],
+                        ),
+                    ],
+                  ),
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/about_page.dart b/lib/ui/screens/about_page.dart
new file mode 100644
index 0000000000000000000000000000000000000000..39efd8cc91f36d4d43227ff614a00e5bb1c60ddf
--- /dev/null
+++ b/lib/ui/screens/about_page.dart
@@ -0,0 +1,41 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+
+import 'package:minehunter/ui/widgets/header_app.dart';
+
+class AboutPage extends StatelessWidget {
+  const AboutPage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(horizontal: 8),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: <Widget>[
+          const SizedBox(height: 8),
+          const AppHeader(text: 'about_title'),
+          const Text('about_content').tr(),
+          FutureBuilder<PackageInfo>(
+            future: PackageInfo.fromPlatform(),
+            builder: (context, snapshot) {
+              switch (snapshot.connectionState) {
+                case ConnectionState.done:
+                  return const Text('about_version').tr(
+                    namedArgs: {
+                      'version': snapshot.data!.version,
+                    },
+                  );
+                default:
+                  return const SizedBox();
+              }
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/screens/home.dart b/lib/ui/screens/game_page.dart
similarity index 61%
rename from lib/screens/home.dart
rename to lib/ui/screens/game_page.dart
index fa7236ac84c423b7ed4f47debfbc436ed7e6050a..cce017474d37d22a29b137981cbdd6e2bd3eb8ae 100644
--- a/lib/screens/home.dart
+++ b/lib/ui/screens/game_page.dart
@@ -1,27 +1,23 @@
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
-import 'package:overlay_support/overlay_support.dart';
 
-import 'package:minehunter/layout/game.dart';
-import 'package:minehunter/layout/parameters.dart';
 import 'package:minehunter/provider/data.dart';
-import 'package:minehunter/utils/game_utils.dart';
+import 'package:minehunter/ui/layout/game.dart';
+import 'package:minehunter/ui/layout/parameters.dart';
 
-class Home extends StatefulWidget {
-  const Home({super.key});
-
-  static const String id = 'home';
+class GamePage extends StatefulWidget {
+  const GamePage({super.key});
 
   @override
-  HomeState createState() => HomeState();
+  GamePageState createState() => GamePageState();
 }
 
-class HomeState extends State<Home> {
+class GamePageState extends State<GamePage> {
   @override
   void initState() {
     super.initState();
 
-    Data myProvider = Provider.of<Data>(context, listen: false);
+    final Data myProvider = Provider.of<Data>(context, listen: false);
     myProvider.initParametersValues();
   }
 
@@ -84,29 +80,11 @@ class HomeState extends State<Home> {
       myProvider.updateAssetsPreloaded(true);
     }
 
-    final List<Widget> menuActions = [];
-
-    if (myProvider.gameIsRunning) {
-      menuActions.add(TextButton(
-        child: const Image(
-          image: AssetImage('assets/icons/button_back.png'),
-          fit: BoxFit.fill,
-        ),
-        onPressed: () => toast('Long press to quit game...'),
-        onLongPress: () => GameUtils.quitGame(myProvider),
-      ));
-    }
-
-    return Scaffold(
-      appBar: AppBar(
-        actions: menuActions,
-      ),
-      body: SafeArea(
-        child: Center(
-          child: myProvider.gameIsRunning
-              ? Game.buildGameWidget(myProvider)
-              : Parameters.buildParametersSelector(myProvider),
-        ),
+    return SafeArea(
+      child: Center(
+        child: myProvider.gameIsRunning
+            ? Game(myProvider: myProvider)
+            : Parameters(myProvider: myProvider),
       ),
     );
   }
diff --git a/lib/ui/screens/settings_page.dart b/lib/ui/screens/settings_page.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7f2146872b3e8a613ea407c3d3517d6c540e026e
--- /dev/null
+++ b/lib/ui/screens/settings_page.dart
@@ -0,0 +1,26 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/ui/widgets/header_app.dart';
+import 'package:minehunter/ui/widgets/settings/settings_form.dart';
+
+class SettingsPage extends StatelessWidget {
+  const SettingsPage({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return const Padding(
+      padding: EdgeInsets.symmetric(horizontal: 8),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: <Widget>[
+          SizedBox(height: 8),
+          AppHeader(text: 'settings_title'),
+          SizedBox(height: 8),
+          SettingsForm(),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6e732da251c47f722436a90562c5c6c4ab2d484e
--- /dev/null
+++ b/lib/ui/skeleton.dart
@@ -0,0 +1,49 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_swipe/flutter_swipe.dart';
+import 'package:provider/provider.dart';
+
+import 'package:minehunter/config/menu.dart';
+import 'package:minehunter/cubit/bottom_nav_cubit.dart';
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/widgets/app_bar.dart';
+import 'package:minehunter/ui/widgets/bottom_nav_bar.dart';
+
+class SkeletonScreen extends StatefulWidget {
+  const SkeletonScreen({super.key});
+
+  @override
+  State<SkeletonScreen> createState() => _SkeletonScreenState();
+}
+
+class _SkeletonScreenState extends State<SkeletonScreen> {
+  @override
+  Widget build(BuildContext context) {
+    final Data myProvider = Provider.of<Data>(context);
+
+    return Scaffold(
+      extendBodyBehindAppBar: false,
+      appBar: StandardAppBar(myProvider: myProvider),
+      body: Swiper(
+        itemCount: Menu.itemsCount,
+        itemBuilder: (BuildContext context, int index) {
+          return Menu.getPageWidget(index);
+        },
+        pagination: SwiperPagination(
+          margin: const EdgeInsets.all(0),
+          builder: SwiperCustomPagination(
+            builder: (BuildContext context, SwiperPluginConfig config) {
+              return BottomNavBar(swipeController: config.controller);
+            },
+          ),
+        ),
+        onIndexChanged: (newPageIndex) {
+          BlocProvider.of<BottomNavCubit>(context).updateIndex(newPageIndex);
+        },
+        outer: true,
+        loop: false,
+      ),
+      backgroundColor: Theme.of(context).colorScheme.background,
+    );
+  }
+}
diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c53fa30eb979bd30e677d20e7bd09b146328f47e
--- /dev/null
+++ b/lib/ui/widgets/app_bar.dart
@@ -0,0 +1,37 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:overlay_support/overlay_support.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/widgets/header_app.dart';
+import 'package:minehunter/utils/game_utils.dart';
+
+class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
+  const StandardAppBar({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final List<Widget> menuActions = [];
+
+    if (myProvider.gameIsRunning) {
+      menuActions.add(TextButton(
+        child: const Image(
+          image: AssetImage('assets/icons/button_back.png'),
+          fit: BoxFit.fill,
+        ),
+        onPressed: () => toast(tr('long_press_to_quit')),
+        onLongPress: () => GameUtils.quitGame(myProvider),
+      ));
+    }
+
+    return AppBar(
+      title: const AppHeader(text: 'app_name'),
+      actions: menuActions,
+    );
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(50);
+}
diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..341d93ea63a3808dfc9aa8e079bfc6b8d13430ff
--- /dev/null
+++ b/lib/ui/widgets/bottom_nav_bar.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_swipe/flutter_swipe.dart';
+
+import 'package:minehunter/config/menu.dart';
+import 'package:minehunter/cubit/bottom_nav_cubit.dart';
+
+class BottomNavBar extends StatelessWidget {
+  const BottomNavBar({super.key, required this.swipeController});
+
+  final SwiperController swipeController;
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+      margin: const EdgeInsets.all(0),
+      elevation: 4,
+      shadowColor: Theme.of(context).colorScheme.shadow,
+      color: Theme.of(context).colorScheme.surfaceVariant,
+      shape: const ContinuousRectangleBorder(),
+      child: BlocBuilder<BottomNavCubit, int>(
+        builder: (BuildContext context, int state) {
+          return BottomNavigationBar(
+            currentIndex: state,
+            onTap: (int index) {
+              context.read<BottomNavCubit>().updateIndex(index);
+              swipeController.move(index);
+            },
+            type: BottomNavigationBarType.fixed,
+            elevation: 0,
+            backgroundColor: Colors.transparent,
+            selectedItemColor: Theme.of(context).colorScheme.primary,
+            unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color,
+            items: Menu.getMenuItems(),
+          );
+        },
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/indicator_top.dart b/lib/ui/widgets/game/indicator_top.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bf9ea9af42c4b24332880420b92b73d0db0a97dd
--- /dev/null
+++ b/lib/ui/widgets/game/indicator_top.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/utils/board_utils.dart';
+
+class TopIndicator extends StatelessWidget {
+  const TopIndicator({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final int flaggedCellsCount = BoardUtils.countFlaggedCells(myProvider.board);
+    final int minesCount = myProvider.minesCount;
+    const double blockSize = 40;
+
+    final Image flagIconBlock = Image(
+      image: AssetImage('assets/skins/${myProvider.parameterSkin}_tile_flag.png'),
+      fit: BoxFit.fill,
+      height: blockSize,
+      width: blockSize,
+    );
+    final Image mineIconBlock = Image(
+      image: AssetImage('assets/skins/${myProvider.parameterSkin}_tile_mine.png'),
+      fit: BoxFit.fill,
+      height: blockSize,
+      width: blockSize,
+    );
+    final Text markedMinesCountBlock = Text(
+      flaggedCellsCount.toString(),
+      style: TextStyle(
+        fontSize: blockSize,
+        fontWeight: FontWeight.bold,
+        color: Theme.of(context).colorScheme.primary,
+      ),
+    );
+    final Text placedMinesCountBlock = Text(
+      minesCount.toString(),
+      style: TextStyle(
+        fontSize: blockSize,
+        fontWeight: FontWeight.bold,
+        color: Theme.of(context).colorScheme.primary,
+      ),
+    );
+
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      crossAxisAlignment: CrossAxisAlignment.center,
+      children: [
+        Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: [
+            markedMinesCountBlock,
+            flagIconBlock,
+            const SizedBox(width: blockSize * 2),
+            mineIconBlock,
+            placedMinesCountBlock,
+          ],
+        )
+      ],
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/message_game_end.dart b/lib/ui/widgets/game/message_game_end.dart
new file mode 100644
index 0000000000000000000000000000000000000000..9eff7f183a81f104fd60228649a1a8745c525a2a
--- /dev/null
+++ b/lib/ui/widgets/game/message_game_end.dart
@@ -0,0 +1,49 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/widgets/home/button_game_restart.dart';
+
+class EndGameMessage extends StatelessWidget {
+  const EndGameMessage({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final Image decorationImage = Image(
+      image: AssetImage(myProvider.gameWin
+          ? 'assets/icons/game_win.png'
+          : myProvider.gameFail
+              ? 'assets/icons/game_fail.png'
+              : ''),
+      fit: BoxFit.fill,
+    );
+
+    return Container(
+      margin: const EdgeInsets.all(2),
+      padding: const EdgeInsets.all(2),
+      child: Table(
+        defaultColumnWidth: const IntrinsicColumnWidth(),
+        children: [
+          TableRow(
+            children: [
+              Column(
+                children: [decorationImage],
+              ),
+              Column(
+                children: [
+                  myProvider.animationInProgress
+                      ? decorationImage
+                      : RestartGameButton(myProvider: myProvider)
+                ],
+              ),
+              Column(
+                children: [decorationImage],
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/mode_button_toggle.dart b/lib/ui/widgets/game/mode_button_toggle.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7f6f7aae3c52a5725871fc8839a07e5c03f4582d
--- /dev/null
+++ b/lib/ui/widgets/game/mode_button_toggle.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+
+class ToggleGameModeButton extends StatelessWidget {
+  const ToggleGameModeButton({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final String reportModeSuffix = myProvider.reportMode ? 'on' : 'off';
+
+    return TextButton(
+      child: Image(
+        image: AssetImage(
+            'assets/skins/${myProvider.parameterSkin}_button_mark_mine_$reportModeSuffix.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () => myProvider.updateReportMode(!myProvider.reportMode),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/mode_indicator_report.dart b/lib/ui/widgets/game/mode_indicator_report.dart
new file mode 100644
index 0000000000000000000000000000000000000000..aac8757d3e808c3a8e98d0d9a561ffcb3f2e06d0
--- /dev/null
+++ b/lib/ui/widgets/game/mode_indicator_report.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+
+class GameModeIndicatorReport extends StatelessWidget {
+  const GameModeIndicatorReport({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+ final    String reportModeSuffix = myProvider.reportMode ? 'on' : 'off';
+
+    return TextButton(
+      child: Image(
+        image: AssetImage(
+            'assets/skins/${myProvider.parameterSkin}_indicator_report_$reportModeSuffix.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () => myProvider.updateReportMode(true),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/mode_indicator_walk.dart b/lib/ui/widgets/game/mode_indicator_walk.dart
new file mode 100644
index 0000000000000000000000000000000000000000..be4cef83e9adce5e990bbc0106f8267b7c22d1ed
--- /dev/null
+++ b/lib/ui/widgets/game/mode_indicator_walk.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+
+class GameModeIndicatorWalk extends StatelessWidget {
+  const GameModeIndicatorWalk({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final String reportModeSuffix = myProvider.reportMode ? 'off' : 'on';
+
+    return TextButton(
+      child: Image(
+        image: AssetImage(
+            'assets/skins/${myProvider.parameterSkin}_indicator_walk_$reportModeSuffix.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () => myProvider.updateReportMode(false),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/mode_toggle.dart b/lib/ui/widgets/game/mode_toggle.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6a438d6cf16a4791366f4cba64574ea2f73287d6
--- /dev/null
+++ b/lib/ui/widgets/game/mode_toggle.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/widgets/game/mode_button_toggle.dart';
+import 'package:minehunter/ui/widgets/game/mode_indicator_report.dart';
+import 'package:minehunter/ui/widgets/game/mode_indicator_walk.dart';
+
+class ToggleGameMode extends StatelessWidget {
+  const ToggleGameMode({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    final Image paddingBlock = Image(
+      image: AssetImage('assets/skins/${myProvider.parameterSkin}_empty.png'),
+      fit: BoxFit.fill,
+    );
+
+    return Table(
+      defaultColumnWidth: const IntrinsicColumnWidth(),
+      children: [
+        TableRow(
+          children: [
+            TableCell(child: paddingBlock),
+            TableCell(child: GameModeIndicatorWalk(myProvider: myProvider)),
+            TableCell(child: ToggleGameModeButton(myProvider: myProvider)),
+            TableCell(child: GameModeIndicatorReport(myProvider: myProvider)),
+            TableCell(child: paddingBlock),
+          ],
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/widgets/header_app.dart b/lib/ui/widgets/header_app.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bf54b77375fbd0260f876f2885d0572b71715383
--- /dev/null
+++ b/lib/ui/widgets/header_app.dart
@@ -0,0 +1,23 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class AppHeader extends StatelessWidget {
+  const AppHeader({super.key, required this.text});
+
+  final String text;
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text(
+          tr(text),
+          textAlign: TextAlign.start,
+          style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/widgets/home/button_game_restart.dart b/lib/ui/widgets/home/button_game_restart.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2fecb871a622f85764e40a512fbed7948da3a53a
--- /dev/null
+++ b/lib/ui/widgets/home/button_game_restart.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/utils/game_utils.dart';
+
+class RestartGameButton extends StatelessWidget {
+  const RestartGameButton({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/icons/button_back.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () => GameUtils.quitGame(myProvider),
+    );
+  }
+}
diff --git a/lib/ui/widgets/home/button_game_resume.dart b/lib/ui/widgets/home/button_game_resume.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2a1cc71f359762fe09962f62b787912cb7ddf351
--- /dev/null
+++ b/lib/ui/widgets/home/button_game_resume.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/ui/layout/parameters.dart';
+import 'package:minehunter/utils/game_utils.dart';
+
+class ResumeGameButton extends Parameters {
+  const ResumeGameButton({super.key, required super.myProvider});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      margin: const EdgeInsets.all(Parameters.blockMargin),
+      padding: const EdgeInsets.all(Parameters.blockPadding),
+      child: Table(
+        defaultColumnWidth: const IntrinsicColumnWidth(),
+        children: [
+          TableRow(
+            children: [
+              Column(
+                children: [
+                  TextButton(
+                    child: Parameters.buildImageContainerWidget('button_delete_saved_game'),
+                    onPressed: () => GameUtils.deleteSavedGame(myProvider),
+                  ),
+                ],
+              ),
+              Column(
+                children: [
+                  TextButton(
+                    child: Parameters.buildImageContainerWidget('button_resume_game'),
+                    onPressed: () => GameUtils.resumeSavedGame(myProvider),
+                  ),
+                ],
+              ),
+              Parameters.buildDecorationImageWidget(),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/home/button_game_start_new.dart b/lib/ui/widgets/home/button_game_start_new.dart
new file mode 100644
index 0000000000000000000000000000000000000000..1d58c490152db767a5a0a64c5a5ce5655f2ef6ec
--- /dev/null
+++ b/lib/ui/widgets/home/button_game_start_new.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+import 'package:minehunter/provider/data.dart';
+import 'package:minehunter/ui/layout/parameters.dart';
+import 'package:minehunter/utils/game_utils.dart';
+
+class StartNewGameButton extends StatelessWidget {
+  const StartNewGameButton({super.key, required this.myProvider});
+
+  final Data myProvider;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      margin: const EdgeInsets.all(Parameters.blockMargin),
+      padding: const EdgeInsets.all(Parameters.blockPadding),
+      child: Table(
+        defaultColumnWidth: const IntrinsicColumnWidth(),
+        children: [
+          TableRow(
+            children: [
+              Parameters.buildDecorationImageWidget(),
+              Column(
+                children: [
+                  TextButton(
+                    child: Parameters.buildImageContainerWidget('button_start'),
+                    onPressed: () => GameUtils.startNewGame(myProvider),
+                  ),
+                ],
+              ),
+              Parameters.buildDecorationImageWidget(),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/settings/settings_form.dart b/lib/ui/widgets/settings/settings_form.dart
new file mode 100644
index 0000000000000000000000000000000000000000..910c9507b2d9f4778e2e046faa35c93af55a6377
--- /dev/null
+++ b/lib/ui/widgets/settings/settings_form.dart
@@ -0,0 +1,63 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:minehunter/ui/widgets/settings/theme_card.dart';
+
+class SettingsForm extends StatefulWidget {
+  const SettingsForm({super.key});
+
+  @override
+  State<SettingsForm> createState() => _SettingsFormState();
+}
+
+class _SettingsFormState extends State<SettingsForm> {
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.start,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.max,
+      children: <Widget>[
+        // Light/dark theme
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: <Widget>[
+            const Text('settings_label_theme').tr(),
+            const Row(
+              mainAxisAlignment: MainAxisAlignment.end,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                ThemeCard(
+                  mode: ThemeMode.system,
+                  icon: UniconsLine.cog,
+                ),
+                ThemeCard(
+                  mode: ThemeMode.light,
+                  icon: UniconsLine.sun,
+                ),
+                ThemeCard(
+                  mode: ThemeMode.dark,
+                  icon: UniconsLine.moon,
+                )
+              ],
+            ),
+          ],
+        ),
+
+        const SizedBox(height: 16),
+      ],
+    );
+  }
+}
diff --git a/lib/ui/widgets/settings/theme_card.dart b/lib/ui/widgets/settings/theme_card.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f90b335498779f51326e4bc0e12552be4f3c325d
--- /dev/null
+++ b/lib/ui/widgets/settings/theme_card.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:minehunter/cubit/theme_cubit.dart';
+
+class ThemeCard extends StatelessWidget {
+  const ThemeCard({
+    super.key,
+    required this.mode,
+    required this.icon,
+  });
+
+  final IconData icon;
+  final ThemeMode mode;
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<ThemeCubit, ThemeModeState>(
+        builder: (BuildContext context, ThemeModeState state) {
+      return Card(
+        elevation: 2,
+        shadowColor: Theme.of(context).colorScheme.shadow,
+        color: state.themeMode == mode
+            ? Theme.of(context).colorScheme.primary
+            : Theme.of(context).colorScheme.surface,
+        shape: const RoundedRectangleBorder(
+          borderRadius: BorderRadius.all(Radius.circular(12)),
+        ),
+        margin: const EdgeInsets.all(5),
+        child: InkWell(
+          onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme(
+            ThemeModeState(themeMode: mode),
+          ),
+          borderRadius: const BorderRadius.all(Radius.circular(12)),
+          child: Icon(
+            icon,
+            size: 32,
+            color:
+                state.themeMode != mode ? Theme.of(context).colorScheme.primary : Colors.white,
+          ),
+        ),
+      );
+    });
+  }
+}
diff --git a/lib/utils/board_animate.dart b/lib/utils/board_animate.dart
index a85c2de5a9cdb5cb52e9196d5a8773620e7822f9..45e1352a3469ffe2331713491cb2291c53f3dbd4 100644
--- a/lib/utils/board_animate.dart
+++ b/lib/utils/board_animate.dart
@@ -5,15 +5,15 @@ import 'package:minehunter/provider/data.dart';
 
 class BoardAnimate {
   // Start game animation: blinking tiles
-  static List<List<List<bool>>> createStartGameAnimationPatterns(Data myProvider) {
-    final List<List<List<bool>>> patterns = [];
+  static AnimatedBoardSequence createStartGameAnimationPatterns(Data myProvider) {
+    final AnimatedBoardSequence patterns = [];
 
     const int patternsCount = 4;
     final int sizeHorizontal = myProvider.sizeHorizontal;
     final int sizeVertical = myProvider.sizeVertical;
 
     for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
-      final List<List<bool>> pattern = [];
+      final AnimatedBoard pattern = [];
       for (int row = 0; row < sizeVertical; row++) {
         final List<bool> patternRow = [];
         for (int col = 0; col < sizeHorizontal; col++) {
@@ -28,8 +28,8 @@ class BoardAnimate {
   }
 
   // Failed game animation: explosions blowing from exploded mines
-  static List<List<List<bool>>> createExplosionAnimationPatterns(Data myProvider) {
-    final List<List<List<bool>>> patterns = [];
+  static AnimatedBoardSequence createExplosionAnimationPatterns(Data myProvider) {
+    final AnimatedBoardSequence patterns = [];
 
     final int sizeHorizontal = myProvider.sizeHorizontal;
     final int sizeVertical = myProvider.sizeVertical;
@@ -37,7 +37,7 @@ class BoardAnimate {
     final List<List<int>> explodedMines = [];
     for (int row = 0; row < sizeVertical; row++) {
       for (int col = 0; col < sizeHorizontal; col++) {
-        if (myProvider.cells[row][col].isExploded) {
+        if (myProvider.board[row][col].isExploded) {
           explodedMines.add([row, col]);
         }
       }
@@ -49,7 +49,7 @@ class BoardAnimate {
     final int patternsCount = max(sizeHorizontal, sizeVertical);
 
     for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
-      final List<List<bool>> pattern = [];
+      final AnimatedBoard pattern = [];
       for (int row = 0; row < sizeVertical; row++) {
         final List<bool> patternRow = [];
         for (int col = 0; col < sizeHorizontal; col++) {
@@ -70,15 +70,15 @@ class BoardAnimate {
   }
 
   // Win game animation: rotating rays from center
-  static List<List<List<bool>>> createWinGameAnimationPatterns(Data myProvider) {
-    final List<List<List<bool>>> patterns = [];
+  static AnimatedBoardSequence createWinGameAnimationPatterns(Data myProvider) {
+    final AnimatedBoardSequence patterns = [];
 
     const int patternsCount = 20;
     final int sizeHorizontal = myProvider.sizeHorizontal;
     final int sizeVertical = myProvider.sizeVertical;
 
     for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
-      List<List<bool>> pattern = [];
+      AnimatedBoard pattern = [];
       for (int row = 0; row < sizeVertical; row++) {
         List<bool> patternRow = [];
         for (int col = 0; col < sizeHorizontal; col++) {
@@ -94,15 +94,15 @@ class BoardAnimate {
   }
 
   // Default multi-purpose animation: sliding stripes, from top left to right bottom
-  static List<List<List<bool>>> createDefaultAnimationPatterns(Data myProvider) {
-    final List<List<List<bool>>> patterns = [];
+  static AnimatedBoardSequence createDefaultAnimationPatterns(Data myProvider) {
+    final AnimatedBoardSequence patterns = [];
 
     const int patternsCount = 16;
     final int sizeHorizontal = myProvider.sizeHorizontal;
     final int sizeVertical = myProvider.sizeVertical;
 
     for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
-      final List<List<bool>> pattern = [];
+      final AnimatedBoard pattern = [];
       for (int row = 0; row < sizeVertical; row++) {
         final List<bool> patternRow = [];
         for (int col = 0; col < sizeHorizontal; col++) {
@@ -117,7 +117,7 @@ class BoardAnimate {
   }
 
   static void startAnimation(Data myProvider, String animationType) {
-    List<List<List<bool>>> patterns = [];
+    AnimatedBoardSequence patterns = [];
 
     switch (animationType) {
       case 'start':
diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart
index de04d0782a40fddf6cb39b0f43b2c2c04a2cfffb..695d5bf72c84b1a53c86b7ea1785e5da9e520e1d 100644
--- a/lib/utils/board_utils.dart
+++ b/lib/utils/board_utils.dart
@@ -45,17 +45,17 @@ class BoardUtils {
     printlog('');
   }
 
-  static List<List<Cell>> createEmptyBoard(int sizeHorizontal, int sizeVertical) {
-    final List<List<Cell>> cells = [];
+  static Board createEmptyBoard(int sizeHorizontal, int sizeVertical) {
+    final Board board = [];
     for (int rowIndex = 0; rowIndex < sizeVertical; rowIndex++) {
       final List<Cell> row = [];
       for (int colIndex = 0; colIndex < sizeHorizontal; colIndex++) {
         row.add(Cell(false));
       }
-      cells.add(row);
+      board.add(row);
     }
 
-    return cells;
+    return board;
   }
 
   static int getMinesCount(int sizeHorizontal, int sizeVertical, String level) {
@@ -93,11 +93,11 @@ class BoardUtils {
   static void createInitialEmptyBoard(Data myProvider) {
     myProvider.updateIsBoardMined(false);
     myProvider
-        .updateCells(createEmptyBoard(myProvider.sizeHorizontal, myProvider.sizeVertical));
+        .updateBoard(createEmptyBoard(myProvider.sizeHorizontal, myProvider.sizeVertical));
   }
 
-  static List<List<Cell>> createBoard(Data myProvider, int forbiddenRow, int forbiddenCol) {
-    final List<List<Cell>> cells = myProvider.cells;
+  static Board createBoard(Data myProvider, int forbiddenRow, int forbiddenCol) {
+    final Board board = myProvider.board;
     final int sizeHorizontal = myProvider.sizeHorizontal;
     final int sizeVertical = myProvider.sizeVertical;
 
@@ -114,23 +114,23 @@ class BoardUtils {
 
     // Put random mines on board
     for (int mineIndex = 0; mineIndex < myProvider.minesCount; mineIndex++) {
-      cells[allowedCells[mineIndex][0]][allowedCells[mineIndex][1]].isMined = true;
+      board[allowedCells[mineIndex][0]][allowedCells[mineIndex][1]].isMined = true;
     }
 
     // Compute all mines counts on cells
     for (int row = 0; row < sizeVertical; row++) {
       for (int col = 0; col < sizeHorizontal; col++) {
-        cells[row][col].minesCountAround = getMinesCountAround(cells, row, col);
+        board[row][col].minesCountAround = getMinesCountAround(board, row, col);
       }
     }
 
-    printGrid(cells);
+    printGrid(board);
 
-    return cells;
+    return board;
   }
 
-  static List<List<Cell>> createBoardFromSavedState(Data myProvider, String savedBoard) {
-    final List<List<Cell>> board = [];
+  static Board createBoardFromSavedState(Data myProvider, String savedBoard) {
+    final Board board = [];
     final int boardSize = pow((savedBoard.length / 6), 1 / 2).round();
     final String boardSizeAsString = '${boardSize}x$boardSize';
     myProvider.updateParameterSize(boardSizeAsString);
@@ -163,7 +163,7 @@ class BoardUtils {
   }
 
   static void reportCell(Data myProvider, int row, int col) {
-    if (!myProvider.cells[row][col].isExplored) {
+    if (!myProvider.board[row][col].isExplored) {
       myProvider.toggleCellMark(row, col);
     }
   }
@@ -171,12 +171,12 @@ class BoardUtils {
   static void walkOnCell(Data myProvider, int row, int col) {
     myProvider.setCellAsExplored(row, col);
 
-    if (myProvider.cells[row][col].minesCountAround == 0) {
-      final List<List<int>> safeCells = getAllSafeCellsAround(myProvider.cells, row, col);
+    if (myProvider.board[row][col].minesCountAround == 0) {
+      final List<List<int>> safeCells = getAllSafeCellsAround(myProvider.board, row, col);
       for (int safeCellIndex = 0; safeCellIndex < safeCells.length; safeCellIndex++) {
         final int safeCellRow = safeCells[safeCellIndex][0];
         final int safeCellCol = safeCells[safeCellIndex][1];
-        if (!myProvider.cells[safeCellRow][safeCellCol].isExplored) {
+        if (!myProvider.board[safeCellRow][safeCellCol].isExplored) {
           walkOnCell(myProvider, safeCellRow, safeCellCol);
         }
       }
@@ -225,11 +225,11 @@ class BoardUtils {
   }
 
   static bool checkGameIsFinished(Data myProvider) {
-    final List<List<Cell>> cells = myProvider.cells;
-    final int sizeHorizontal = cells.length;
-    final int sizeVertical = cells[0].length;
+    final Board board = myProvider.board;
+    final int sizeHorizontal = board.length;
+    final int sizeVertical = board[0].length;
 
-    printGrid(cells);
+    printGrid(board);
 
     myProvider.updateGameWin(false);
     myProvider.updateGameFail(false);
@@ -237,7 +237,7 @@ class BoardUtils {
     for (int row = 0; row < sizeVertical; row++) {
       for (int col = 0; col < sizeHorizontal; col++) {
         // Walked on a mine
-        if (cells[row][col].isExploded == true) {
+        if (board[row][col].isExploded == true) {
           myProvider.updateGameFail(true);
           return true;
         }
@@ -248,9 +248,9 @@ class BoardUtils {
       for (int col = 0; col < sizeHorizontal; col++) {
         if (
             // Mine not already found
-            (cells[row][col].isMined == true && cells[row][col].isMarked == false) ||
+            (board[row][col].isMined == true && board[row][col].isMarked == false) ||
                 // Safe cell marked as mined
-                (cells[row][col].isMined == false && cells[row][col].isMarked == true)) {
+                (board[row][col].isMined == false && board[row][col].isMarked == true)) {
           return false;
         }
       }
diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart
index 42bc51a2e3f3d5d6585cde10c68279056b48c419..383a287ce97a37702e808ecc9412a8da134e3b95 100644
--- a/lib/utils/game_utils.dart
+++ b/lib/utils/game_utils.dart
@@ -33,7 +33,7 @@ class GameUtils {
         myProvider.setParameterValue('size', savedState['size']);
         myProvider.setParameterValue('skin', savedState['skin']);
 
-        myProvider.updateCells(
+        myProvider.updateBoard(
             BoardUtils.createBoardFromSavedState(myProvider, savedState['board']));
         myProvider.updateGameIsRunning(true);
       } catch (e) {
diff --git a/pubspec.lock b/pubspec.lock
index 6b1fdfef808496ebd4ff8ce5bb9e51bfbc6469e8..2885f1ae8482614c38711b57e048f2bd0e10e178 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,14 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
+  args:
+    dependency: transitive
+    description:
+      name: args
+      sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.2"
   async:
     dependency: transitive
     description:
@@ -9,6 +17,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.11.0"
+  bloc:
+    dependency: transitive
+    description:
+      name: bloc
+      sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.3"
   characters:
     dependency: transitive
     description:
@@ -17,6 +33,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.3.0"
+  clock:
+    dependency: transitive
+    description:
+      name: clock
+      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
   collection:
     dependency: transitive
     description:
@@ -25,6 +49,38 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.18.0"
+  crypto:
+    dependency: transitive
+    description:
+      name: crypto
+      sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.3"
+  easy_localization:
+    dependency: "direct main"
+    description:
+      name: easy_localization
+      sha256: c145aeb6584aedc7c862ab8c737c3277788f47488bfdf9bae0fe112bd0a4789c
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.5"
+  easy_logger:
+    dependency: transitive
+    description:
+      name: easy_logger
+      sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.0.2"
+  equatable:
+    dependency: "direct main"
+    description:
+      name: equatable
+      sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.5"
   ffi:
     dependency: transitive
     description:
@@ -46,6 +102,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_bloc:
+    dependency: "direct main"
+    description:
+      name: flutter_bloc
+      sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.4"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -54,11 +118,64 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.0.1"
+  flutter_localizations:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
+  flutter_swipe:
+    dependency: "direct main"
+    description:
+      name: flutter_swipe
+      sha256: dc6541bac3a0545ce15a3fa15913f6250532062960bf6b0ad4562d02f14a8545
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.1"
   flutter_web_plugins:
     dependency: transitive
     description: flutter
     source: sdk
     version: "0.0.0"
+  hive:
+    dependency: "direct main"
+    description:
+      name: hive
+      sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.3"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.0"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.0.2"
+  hydrated_bloc:
+    dependency: "direct main"
+    description:
+      name: hydrated_bloc
+      sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "9.1.4"
+  intl:
+    dependency: transitive
+    description:
+      name: intl
+      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.18.1"
   lints:
     dependency: transitive
     description:
@@ -99,14 +216,54 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.1.0"
-  path:
+  package_info_plus:
+    dependency: "direct main"
+    description:
+      name: package_info_plus
+      sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.0.1"
+  package_info_plus_platform_interface:
     dependency: transitive
+    description:
+      name: package_info_plus_platform_interface
+      sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.1"
+  path:
+    dependency: "direct main"
     description:
       name: path
       sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
       url: "https://pub.dev"
     source: hosted
     version: "1.9.0"
+  path_provider:
+    dependency: "direct main"
+    description:
+      name: path_provider
+      sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.2"
+  path_provider_android:
+    dependency: transitive
+    description:
+      name: path_provider_android
+      sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.2"
+  path_provider_foundation:
+    dependency: transitive
+    description:
+      name: path_provider_foundation
+      sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.3.2"
   path_provider_linux:
     dependency: transitive
     description:
@@ -199,10 +356,10 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_web
-      sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
+      sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.0"
+    version: "2.2.2"
   shared_preferences_windows:
     dependency: transitive
     description:
@@ -216,6 +373,54 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.99"
+  source_span:
+    dependency: transitive
+    description:
+      name: source_span
+      sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.10.0"
+  string_scanner:
+    dependency: transitive
+    description:
+      name: string_scanner
+      sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.0"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.0+1"
+  term_glyph:
+    dependency: transitive
+    description:
+      name: term_glyph
+      sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.1"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.3.2"
+  unicons:
+    dependency: "direct main"
+    description:
+      name: unicons
+      sha256: dbfcf93ff4d4ea19b324113857e358e4882115ab85db04417a4ba1c72b17a670
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.1"
   vector_math:
     dependency: transitive
     description:
@@ -228,10 +433,10 @@ packages:
     dependency: transitive
     description:
       name: web
-      sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
+      sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05"
       url: "https://pub.dev"
     source: hosted
-    version: "0.5.0"
+    version: "0.4.2"
   win32:
     dependency: transitive
     description:
@@ -249,5 +454,5 @@ packages:
     source: hosted
     version: "1.0.4"
 sdks:
-  dart: ">=3.3.0 <4.0.0"
-  flutter: ">=3.19.0"
+  dart: ">=3.3.0-279.1.beta <4.0.0"
+  flutter: ">=3.16.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index ca6e37179087ec5f58ecadc55923d48b32665e30..72d953f7bb217acdb47a62d5ecd83fb9aa33a945 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,8 @@
 name: minehunter
 description: A minehunter game application.
+
 publish_to: 'none'
-version: 0.1.16+37
+version: 0.1.17+38
 
 environment:
   sdk: '^3.0.0'
@@ -9,9 +10,20 @@ environment:
 dependencies:
   flutter:
     sdk: flutter
+
+  easy_localization: ^3.0.1
+  equatable: ^2.0.5
+  flutter_bloc: ^8.1.1
+  flutter_swipe: ^1.0.1
+  hive: ^2.2.3
+  hydrated_bloc: ^9.0.0
+  overlay_support: ^2.1.0
   provider: ^6.0.5
   shared_preferences: ^2.2.1
-  overlay_support: ^2.1.0
+  package_info_plus: ^5.0.1
+  path: ^1.9.0
+  path_provider: ^2.0.11
+  unicons: ^2.1.1
 
 dev_dependencies:
   flutter_lints: ^3.0.1
@@ -21,3 +33,17 @@ flutter:
   assets:
     - assets/icons/
     - assets/skins/
+    - assets/translations/
+
+  fonts:
+    - family: Nunito
+      fonts:
+        - asset: assets/fonts/Nunito-Bold.ttf
+          weight: 700
+        - asset: assets/fonts/Nunito-Medium.ttf
+          weight: 500
+        - asset: assets/fonts/Nunito-Regular.ttf
+          weight: 400
+        - asset: assets/fonts/Nunito-Light.ttf
+          weight: 300
+