diff --git a/android/app/build.gradle b/android/app/build.gradle
index 56c75471c2ba92bfccfdc5d0b8b7d6bdef991e21..ba7c075900cf22911bcd631e4cc428a72e2cd6c7 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -44,7 +44,7 @@ android {
 
     defaultConfig {
         applicationId "org.benoitharrault.jeweled"
-        minSdkVersion 16
+        minSdkVersion flutter.minSdkVersion
         targetSdkVersion 30
         versionCode appVersionCode.toInteger()
         versionName appVersionName
diff --git a/android/gradle.properties b/android/gradle.properties
index 65eed6426393974efb5a056ec44936d42b5ef2a1..4bb5439f682100f8ef4ba80a557fe4f2f0ab14c2 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.8
-app.versionCode=8
+app.versionName=0.0.9
+app.versionCode=9
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/icons/size_medium.png b/assets/icons/boardSize_10.png
similarity index 100%
rename from assets/icons/size_medium.png
rename to assets/icons/boardSize_10.png
diff --git a/assets/icons/size_large.png b/assets/icons/boardSize_14.png
similarity index 100%
rename from assets/icons/size_large.png
rename to assets/icons/boardSize_14.png
diff --git a/assets/icons/size_extra.png b/assets/icons/boardSize_20.png
similarity index 100%
rename from assets/icons/size_extra.png
rename to assets/icons/boardSize_20.png
diff --git a/assets/icons/size_small.png b/assets/icons/boardSize_6.png
similarity index 100%
rename from assets/icons/size_small.png
rename to assets/icons/boardSize_6.png
diff --git a/assets/icons/colors_5.png b/assets/icons/colorsCount_5.png
similarity index 100%
rename from assets/icons/colors_5.png
rename to assets/icons/colorsCount_5.png
diff --git a/assets/icons/colors_6.png b/assets/icons/colorsCount_6.png
similarity index 100%
rename from assets/icons/colors_6.png
rename to assets/icons/colorsCount_6.png
diff --git a/assets/icons/colors_7.png b/assets/icons/colorsCount_7.png
similarity index 100%
rename from assets/icons/colors_7.png
rename to assets/icons/colorsCount_7.png
diff --git a/assets/icons/colors_8.png b/assets/icons/colorsCount_8.png
similarity index 100%
rename from assets/icons/colors_8.png
rename to assets/icons/colorsCount_8.png
diff --git a/assets/icons/level_easy.png b/assets/icons/level_easy.png
deleted file mode 100644
index aa9e2485272c12fd99d9e1faae5d71d027ee5431..0000000000000000000000000000000000000000
Binary files a/assets/icons/level_easy.png and /dev/null differ
diff --git a/assets/icons/level_hard.png b/assets/icons/level_hard.png
deleted file mode 100644
index 433c686b984fd22c4966def3dedcaf7384951810..0000000000000000000000000000000000000000
Binary files a/assets/icons/level_hard.png and /dev/null differ
diff --git a/assets/icons/level_medium.png b/assets/icons/level_medium.png
deleted file mode 100644
index 213f2ca844afb0cea47cc4bdd1b865433b068b59..0000000000000000000000000000000000000000
Binary files a/assets/icons/level_medium.png and /dev/null differ
diff --git a/assets/icons/level_nightmare.png b/assets/icons/level_nightmare.png
deleted file mode 100644
index df4182c1e228bbb33c5546d2df38d16ca1edd4f2..0000000000000000000000000000000000000000
Binary files a/assets/icons/level_nightmare.png and /dev/null differ
diff --git a/assets/translations/en.json b/assets/translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..098a873d7b542216c2369f7618a54e5f9ec4e131
--- /dev/null
+++ b/assets/translations/en.json
@@ -0,0 +1,3 @@
+{
+  "app_name": "Jeweled"
+}
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..098a873d7b542216c2369f7618a54e5f9ec4e131
--- /dev/null
+++ b/assets/translations/fr.json
@@ -0,0 +1,3 @@
+{
+  "app_name": "Jeweled"
+}
diff --git a/fastlane/metadata/android/en-US/changelogs/9.txt b/fastlane/metadata/android/en-US/changelogs/9.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f5fd8646c2688aac7044cb783cc9855ad6a0bc06
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/9.txt
@@ -0,0 +1 @@
+Improve game architecture, massive code rewrite.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/9.txt b/fastlane/metadata/android/fr-FR/changelogs/9.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cb5853858f0262e07da6c8e75a1551e7f55b53cb
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/9.txt
@@ -0,0 +1 @@
+Amélioration globale de l'architecture du jeu, réécriture massive de code.
diff --git a/icons/size_medium.svg b/icons/boardSize_10.svg
similarity index 100%
rename from icons/size_medium.svg
rename to icons/boardSize_10.svg
diff --git a/icons/size_large.svg b/icons/boardSize_14.svg
similarity index 100%
rename from icons/size_large.svg
rename to icons/boardSize_14.svg
diff --git a/icons/size_extra.svg b/icons/boardSize_20.svg
similarity index 100%
rename from icons/size_extra.svg
rename to icons/boardSize_20.svg
diff --git a/icons/size_small.svg b/icons/boardSize_6.svg
similarity index 100%
rename from icons/size_small.svg
rename to icons/boardSize_6.svg
diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh
index 08f5571f42f74293ef170e2eac8e7cc59f740670..f86f10bce481975d4de944ff92c06a1d04d5773f 100755
--- a/icons/build_game_icons.sh
+++ b/icons/build_game_icons.sh
@@ -27,9 +27,8 @@ AVAILABLE_GAME_IMAGES="
 
 # Settings images
 AVAILABLES_GAME_SETTINGS="
-  level:easy,medium,hard,nightmare
-  size:small,medium,large,extra
-  colors:5,6,7,8
+  boardSize:6,10,14,20
+  colorsCount:5,6,7,8
 "
 
 #######################################################
diff --git a/icons/colors_5.svg b/icons/colorsCount_5.svg
similarity index 100%
rename from icons/colors_5.svg
rename to icons/colorsCount_5.svg
diff --git a/icons/colors_6.svg b/icons/colorsCount_6.svg
similarity index 100%
rename from icons/colors_6.svg
rename to icons/colorsCount_6.svg
diff --git a/icons/colors_7.svg b/icons/colorsCount_7.svg
similarity index 100%
rename from icons/colors_7.svg
rename to icons/colorsCount_7.svg
diff --git a/icons/colors_8.svg b/icons/colorsCount_8.svg
similarity index 100%
rename from icons/colors_8.svg
rename to icons/colorsCount_8.svg
diff --git a/icons/level_easy.svg b/icons/level_easy.svg
deleted file mode 100644
index 30048ce976f10fba1ad4233115035786a65d35de..0000000000000000000000000000000000000000
--- a/icons/level_easy.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#97c05c" stroke="#000" stroke-width="2"/><path d="m50.952 32.393c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133s-9.3059 8.2444-9.7225 9.5414c-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434c-0.42533-1.2941-10.203-8.1785-9.7868-9.4754 0.41657-1.297 12.375-1.2 13.474-2.0044s4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#030303" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/></svg>
diff --git a/icons/level_hard.svg b/icons/level_hard.svg
deleted file mode 100644
index 976249e8b0d2274b791d00e128be518d29a03731..0000000000000000000000000000000000000000
--- a/icons/level_hard.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#cd5542" stroke="#000" stroke-width="2"/><path d="m28.065 11.952c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133s-9.3059 8.2444-9.7225 9.5414c-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434c-0.42533-1.2941-10.203-8.1785-9.7868-9.4754 0.41657-1.297 12.375-1.2 13.474-2.0044s4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#010101" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m73.839 11.952c1.3622-0.0046 4.9652 11.398 6.07 12.195s13.062 0.61914 13.487 1.9133c0.42533 1.2941-9.3059 8.2444-9.7225 9.5414-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434c-0.42533-1.2941-10.203-8.1785-9.7868-9.4754 0.41657-1.297 12.375-1.2 13.474-2.0044 1.0993-0.80441 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#010101" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m50.952 52.835c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133 0.42533 1.2941-9.3059 8.2444-9.7225 9.5414-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 5e-3 -10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434-10.203-8.1785-9.7868-9.4754c0.41657-1.297 12.375-1.2 13.474-2.0044 1.0993-0.80442 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#010101" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/></svg>
diff --git a/icons/level_medium.svg b/icons/level_medium.svg
deleted file mode 100644
index e70fd60d179b05a0db5701e4b7858c214887c6be..0000000000000000000000000000000000000000
--- a/icons/level_medium.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#f29c38" stroke="#000" stroke-width="2"/><path d="m27.72 32.393c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133s-9.3059 8.2444-9.7225 9.5414c-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434c-0.42533-1.2941-10.203-8.1785-9.7868-9.4754 0.41657-1.297 12.375-1.2 13.474-2.0044s4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m74.183 32.393c1.3622-0.0046 4.9652 11.398 6.07 12.195s13.062 0.61914 13.487 1.9133-9.3059 8.2444-9.7225 9.5414c-0.41656 1.297 3.4475 12.614 2.3482 13.418-1.0994 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797-1.1048-0.79696 2.6827-12.14 2.2574-13.434s-10.203-8.1785-9.7868-9.4754c0.41657-1.297 12.375-1.2 13.474-2.0044 1.0993-0.80441 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/></svg>
diff --git a/icons/level_nightmare.svg b/icons/level_nightmare.svg
deleted file mode 100644
index 87f28a3defc23c6fe95980cb376efbf56d6885cc..0000000000000000000000000000000000000000
--- a/icons/level_nightmare.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#6041b0" stroke="#000" stroke-width="2"/><path d="m28.929 11.793c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133s-9.3059 8.2444-9.7225 9.5414c-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80442-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797-1.1048-0.79696 2.6827-12.14 2.2574-13.434-0.42533-1.2941-10.203-8.1785-9.7868-9.4754 0.41657-1.297 12.375-1.2 13.474-2.0044 1.0993-0.80441 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m73.125 11.861c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133 0.42533 1.2941-9.3059 8.2444-9.7225 9.5414-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80441-10.717-6.3028-12.079-6.2982-1.3622 0.0046-10.931 7.1767-12.036 6.3797s2.6827-12.14 2.2574-13.434-10.203-8.1785-9.7868-9.4754c0.41657-1.297 12.375-1.2 13.474-2.0044 1.0993-0.80442 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m28.778 52.923c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133 0.42533 1.2941-9.3059 8.2444-9.7225 9.5414s3.4475 12.614 2.3481 13.418c-1.0993 0.80442-10.717-6.3028-12.079-6.2982-1.3622 5e-3 -10.931 7.1767-12.036 6.3797-1.1048-0.79697 2.6827-12.14 2.2574-13.434-0.42533-1.2941-10.203-8.1785-9.7868-9.4754s12.375-1.2 13.474-2.0044c1.0993-0.80441 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/><path d="m73.104 52.992c1.3622-0.0046 4.9652 11.398 6.07 12.195 1.1048 0.79696 13.062 0.61914 13.487 1.9133s-9.3059 8.2444-9.7225 9.5414c-0.41657 1.297 3.4475 12.614 2.3481 13.418-1.0993 0.80442-10.717-6.3028-12.079-6.2982-1.3622 5e-3 -10.931 7.1767-12.036 6.3797-1.1048-0.79697 2.6827-12.14 2.2574-13.434-0.42533-1.2941-10.203-8.1785-9.7868-9.4754s12.375-1.2 13.474-2.0044c1.0993-0.80441 4.6252-12.231 5.9874-12.236z" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="6" stroke-width="3.3"/></svg>
diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a8f11fa1d0ae2014a27b912d19ae22efac186a8c
--- /dev/null
+++ b/lib/config/default_game_settings.dart
@@ -0,0 +1,33 @@
+class DefaultGameSettings {
+  static const List<String> availableParameters = [
+    'boardSize',
+    'colorsCount',
+  ];
+
+  static const int defaultBoardSizeValue = 10;
+  static const List<int> allowedBoardSizeValues = [
+    6,
+    10,
+    14,
+    20,
+  ];
+
+  static const int defaultColorsCountValue = 6;
+  static const List<int> allowedColorsCountValues = [
+    5,
+    6,
+    7,
+    8,
+  ];
+
+  static List<int> getAvailableValues(String parameterCode) {
+    switch (parameterCode) {
+      case 'boardSize':
+        return DefaultGameSettings.allowedBoardSizeValues;
+      case 'colorsCount':
+        return DefaultGameSettings.allowedColorsCountValues;
+    }
+
+    return [];
+  }
+}
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/game_cubit.dart b/lib/cubit/game_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..86e9c01600c0e8acadd57612553ab4fd813f1a18
--- /dev/null
+++ b/lib/cubit/game_cubit.dart
@@ -0,0 +1,159 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:jeweled/models/game.dart';
+import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/models/game_settings.dart';
+
+part 'game_state.dart';
+
+class GameCubit extends HydratedCubit<GameState> {
+  GameCubit()
+      : super(GameState(
+          currentGame: Game.createNull(),
+        ));
+
+  void updateState(Game game) {
+    emit(GameState(
+      currentGame: game,
+    ));
+  }
+
+  void refresh() {
+    final Game game = Game(
+      board: state.currentGame.board,
+      settings: state.currentGame.settings,
+      isRunning: state.currentGame.isRunning,
+      isFinished: state.currentGame.isFinished,
+      availableBlocksCount: state.currentGame.availableBlocksCount,
+      movesCount: state.currentGame.movesCount,
+      score: state.currentGame.score,
+    );
+    // game.dump();
+
+    updateState(game);
+  }
+
+  void quitGame() {
+    state.currentGame.updateGameIsRunning(false);
+    refresh();
+  }
+
+  void updateCellValue(CellLocation locationToUpdate, int? value) {
+    state.currentGame.updateCellValue(locationToUpdate, value);
+    refresh();
+  }
+
+  void increaseMovesCount() {
+    this.state.currentGame.increaseMovesCount();
+    refresh();
+  }
+
+  void increaseScore(int increment) {
+    this.state.currentGame.increaseScore(increment);
+    refresh();
+  }
+
+  void updateAvailableBlocksCount() {
+    this.state.currentGame.updateAvailableBlocksCount();
+    refresh();
+  }
+
+  void updateGameIsFinished(bool gameIsFinished) {
+    this.state.currentGame.updateGameIsFinished(gameIsFinished);
+    refresh();
+  }
+
+  moveCellsDown() {
+    final Game currentGame = this.state.currentGame;
+
+    final int boardSizeHorizontal = currentGame.settings.boardSize;
+    final int boardSizeVertical = currentGame.settings.boardSize;
+
+    for (int row = 0; row < boardSizeVertical; row++) {
+      for (int col = 0; col < boardSizeHorizontal; col++) {
+        // empty cell?
+        if (currentGame.getCellValue(CellLocation.go(row, col)) == null) {
+          // move cells down
+          for (int r = row; r > 0; r--) {
+            this.updateCellValue(CellLocation.go(r, col),
+                currentGame.getCellValue(CellLocation.go(r - 1, col)));
+          }
+          // fill top empty cell
+          this.updateCellValue(
+              CellLocation.go(0, col), currentGame.getFillValue(CellLocation.go(row, col)));
+        }
+      }
+    }
+  }
+
+  void deleteBlock(List<CellLocation> block) {
+    // Sort cells from top to bottom
+    block.sort((cell1, cell2) => cell1.row.compareTo(cell2.row));
+    // Delete all cells
+    block.forEach((CellLocation blockItemToDelete) {
+      this.updateCellValue(blockItemToDelete, null);
+    });
+    // Gravity!
+    this.moveCellsDown();
+  }
+
+  int getScoreFromBlock(int blockSize) {
+    return 3 * (blockSize - 2);
+  }
+
+  void 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);
+        this.increaseMovesCount();
+        this.increaseScore(getScoreFromBlock(block.length));
+        this.updateAvailableBlocksCount();
+      }
+    }
+
+    if (!currentGame.hasAtLeastOneAvailableBlock()) {
+      print('no more block found. finish game.');
+      this.updateGameIsFinished(true);
+    }
+  }
+
+  void startNewGame(GameSettings settings) {
+    Game newGame = Game.createNew(
+      gameSettings: settings,
+    );
+
+    newGame.dump();
+
+    updateState(newGame);
+  }
+
+  @override
+  GameState? fromJson(Map<String, dynamic> json) {
+    Game currentGame = json['currentGame'] as Game;
+
+    return GameState(
+      currentGame: currentGame,
+    );
+  }
+
+  @override
+  Map<String, dynamic>? toJson(GameState state) {
+    return <String, dynamic>{
+      'currentGame': state.currentGame.toJson(),
+    };
+  }
+}
diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3fd161a0915313722b7a15c55c7cf538a7e3b6e1
--- /dev/null
+++ b/lib/cubit/game_state.dart
@@ -0,0 +1,19 @@
+part of 'game_cubit.dart';
+
+@immutable
+class GameState extends Equatable {
+  const GameState({
+    required this.currentGame,
+  });
+
+  final Game currentGame;
+
+  @override
+  List<dynamic> get props => <dynamic>[
+        currentGame,
+      ];
+
+  Map<String, dynamic> get values => <String, dynamic>{
+        'currentGame': currentGame,
+      };
+}
diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_cubit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..76b785030f8488d7e61c0486acfaa6ece2b99acf
--- /dev/null
+++ b/lib/cubit/settings_cubit.dart
@@ -0,0 +1,69 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:jeweled/models/game_settings.dart';
+
+part 'settings_state.dart';
+
+class SettingsCubit extends HydratedCubit<SettingsState> {
+  SettingsCubit() : super(SettingsState(settings: GameSettings.createDefault()));
+
+  void setValues({
+    int? boardSize,
+    int? colorsCount,
+  }) {
+    emit(
+      SettingsState(
+        settings: GameSettings(
+          boardSize: boardSize ?? state.settings.boardSize,
+          colorsCount: colorsCount ?? state.settings.colorsCount,
+        ),
+      ),
+    );
+  }
+
+  int getParameterValue(String code) {
+    switch (code) {
+      case 'boardSize':
+        return GameSettings.getBoardSizeValueFromUnsafe(state.settings.boardSize);
+      case 'colorsCount':
+        return GameSettings.getColorsCountValueFromUnsafe(state.settings.colorsCount);
+    }
+    return 0;
+  }
+
+  void setParameterValue(String code, int value) {
+    print('SettingsCubit.setParameterValue');
+    print('code: ' + code + '  / value: ' + value.toString());
+
+    int boardSize = code == 'boardSize' ? value : getParameterValue('boardSize');
+    int colorsCount = code == 'colorsCount' ? value : getParameterValue('colorsCount');
+
+    setValues(
+      boardSize: boardSize,
+      colorsCount: colorsCount,
+    );
+  }
+
+  @override
+  SettingsState? fromJson(Map<String, dynamic> json) {
+    int boardSize = json['boardSize'] as int;
+    int colorsCount = json['colorsCount'] as int;
+
+    return SettingsState(
+      settings: GameSettings(
+        boardSize: boardSize,
+        colorsCount: colorsCount,
+      ),
+    );
+  }
+
+  @override
+  Map<String, dynamic>? toJson(SettingsState state) {
+    return <String, dynamic>{
+      'boardSize': state.settings.boardSize,
+      'colorsCount': state.settings.colorsCount,
+    };
+  }
+}
diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_state.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b7125618710059d16893a9d87e74c6e2565a8603
--- /dev/null
+++ b/lib/cubit/settings_state.dart
@@ -0,0 +1,19 @@
+part of 'settings_cubit.dart';
+
+@immutable
+class SettingsState extends Equatable {
+  const SettingsState({
+    required this.settings,
+  });
+
+  final GameSettings settings;
+
+  @override
+  List<dynamic> get props => <dynamic>[
+        settings,
+      ];
+
+  Map<String, dynamic> get values => <String, dynamic>{
+        'settings': settings,
+      };
+}
diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart
deleted file mode 100644
index 669d9fe59e12eaeea807baf314a941f7056c2ee2..0000000000000000000000000000000000000000
--- a/lib/entities/cell.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-class Cell {
-  String value = '0';
-
-  Cell(
-    this.value,
-  );
-}
diff --git a/lib/layout/board.dart b/lib/layout/board.dart
deleted file mode 100644
index 4cf8f3c5b276527a10b54623c4eab7bda61c8a47..0000000000000000000000000000000000000000
--- a/lib/layout/board.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/layout/board_painter.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/utils/board_utils.dart';
-
-class Board {
-  static Container buildGameBoard(Data myProvider, double boardWidth) {
-    return Container(
-      margin: EdgeInsets.all(4),
-      padding: EdgeInsets.all(4),
-      child: Column(
-        children: [
-          Container(
-            child: Center(
-              child: GestureDetector(
-                onTapUp: (details) {
-                  double xTap = details.localPosition.dx;
-                  double yTap = details.localPosition.dy;
-                  int col = xTap ~/ (boardWidth / myProvider.sizeHorizontal);
-                  int row = yTap ~/ (boardWidth / myProvider.sizeVertical);
-                  BoardUtils.tapOnCell(myProvider, row, col);
-                },
-                child: Container(
-                  child: CustomPaint(
-                    size: Size(boardWidth, boardWidth),
-                    willChange: false,
-                    painter: BoardPainter(myProvider),
-                    isComplex: true,
-                  ),
-                ),
-              ),
-            ),
-          )
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/layout/board_painter.dart b/lib/layout/board_painter.dart
deleted file mode 100644
index a758d3785b3ff877b02437d2c96fe8a2a749665f..0000000000000000000000000000000000000000
--- a/lib/layout/board_painter.dart
+++ /dev/null
@@ -1,88 +0,0 @@
-import 'dart:math';
-
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/entities/cell.dart';
-import 'package:jeweled_game/layout/color_theme.dart';
-import 'package:jeweled_game/provider/data.dart';
-
-class BoardPainter extends CustomPainter {
-  const BoardPainter(this.myProvider);
-
-  final Data myProvider;
-
-  @override
-  void paint(Canvas canvas, Size size) {
-    int sizeHorizontal = myProvider.sizeHorizontal;
-    int sizeVertical = myProvider.sizeVertical;
-
-    List cells = myProvider.cells;
-    double cellSize = size.width / (max(sizeHorizontal, sizeVertical));
-
-    // background
-    for (var row = 0; row < sizeVertical; row++) {
-      double y = cellSize * row;
-      for (var col = 0; col < sizeHorizontal; col++) {
-        double x = cellSize * col;
-
-        final Cell cell = cells[row][col];
-        final String cellValue = cell.value;
-        final int colorCode = ColorTheme.getColorCode(myProvider.parameterSkin, cellValue);
-
-        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);
-      }
-    }
-
-    // borders
-    double borderSize = 4;
-    final cellPaintBorder = Paint();
-    cellPaintBorder.color = Colors.black;
-    cellPaintBorder.strokeWidth = borderSize;
-    cellPaintBorder.strokeCap = StrokeCap.round;
-
-    for (var row = 0; row < sizeVertical; row++) {
-      double y = cellSize * row;
-      for (var col = 0; col < sizeHorizontal; col++) {
-        double x = cellSize * col;
-
-        final Cell cell = cells[row][col];
-        final String cellValue = cell.value;
-
-        if ((row == 0) || (row > 1 && cellValue != myProvider.getCellValue(row - 1, col))) {
-          Offset borderStart = Offset(x, y);
-          Offset borderStop = Offset(x + cellSize, y);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
-        if ((row == sizeVertical - 1) ||
-            ((row + 1) < sizeVertical && cellValue != myProvider.getCellValue(row + 1, col))) {
-          Offset borderStart = Offset(x, y + cellSize);
-          Offset borderStop = Offset(x + cellSize, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
-        if ((col == 0) || (col > 1 && cellValue != myProvider.getCellValue(row, col - 1))) {
-          Offset borderStart = Offset(x, y);
-          Offset borderStop = Offset(x, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
-        if ((col == sizeHorizontal - 1) ||
-            ((col + 1) < sizeHorizontal &&
-                cellValue != myProvider.getCellValue(row, col + 1))) {
-          Offset borderStart = Offset(x + cellSize, y);
-          Offset borderStop = Offset(x + cellSize, y + cellSize);
-          canvas.drawLine(borderStart, borderStop, cellPaintBorder);
-        }
-      }
-    }
-  }
-
-  @override
-  bool shouldRepaint(CustomPainter oldDelegate) {
-    return false;
-  }
-}
diff --git a/lib/layout/color_theme.dart b/lib/layout/color_theme.dart
deleted file mode 100644
index ebc2309c91c0eea47a3827f328f4e07cb4e69503..0000000000000000000000000000000000000000
--- a/lib/layout/color_theme.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-class ColorTheme {
-  static Map<String, Map<String, int>> borderColors = {
-    'default': {
-      '0': 0xffffff,
-      '1': 0xe63a3f,
-      '2': 0x708cfd,
-      '3': 0x359c35,
-      '4': 0xffce2c,
-      '5': 0xff6f43,
-      '6': 0xa13cb1,
-      '7': 0x38ffff,
-      '8': 0xf2739d,
-    },
-  };
-  static int defaultBorderColor = 0x808080;
-
-  static int getColorCode(String skin, String value) {
-    if (borderColors.containsKey(skin) && null != borderColors[skin]) {
-      Map<String, int>? skinColors = borderColors[skin];
-      if (null != skinColors && skinColors.containsKey(value) && null != skinColors[value]) {
-        return (skinColors[value] ?? defaultBorderColor) | 0xFF000000;
-      }
-    }
-    return defaultBorderColor | 0xFF000000;
-  }
-}
diff --git a/lib/layout/game.dart b/lib/layout/game.dart
deleted file mode 100644
index 4360e6a29564252b57fb47dd05f76814224f849f..0000000000000000000000000000000000000000
--- a/lib/layout/game.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/layout/board.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/utils/game_utils.dart';
-
-class Game {
-  static Container buildGameWidget(Data myProvider, double boardWidth) {
-    bool gameIsFinished = myProvider.isGameFinished;
-
-    return Container(
-      child: Column(
-        mainAxisAlignment: MainAxisAlignment.start,
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: [
-          SizedBox(height: 8),
-          Game.buildTopIndicatorWidget(myProvider),
-          SizedBox(height: 2),
-          Expanded(
-            child: Board.buildGameBoard(myProvider, boardWidth),
-          ),
-          SizedBox(height: 2),
-          Container(
-            child: gameIsFinished ? Game.buildEndGameMessage(myProvider) : SizedBox(height: 2),
-          ),
-        ],
-      ),
-    );
-  }
-
-  static Widget buildTopIndicatorWidget(Data myProvider) {
-    return Table(
-      children: [
-        TableRow(
-          children: [
-            Column(
-              children: [
-                Text(
-                  myProvider.score.toString(),
-                  style: TextStyle(
-                    fontSize: 40,
-                    fontWeight: FontWeight.w600,
-                    color: Colors.black,
-                  ),
-                ),
-                Text(
-                  myProvider.movesCount.toString(),
-                  style: TextStyle(
-                    fontSize: 15,
-                    fontWeight: FontWeight.w600,
-                    color: Colors.grey,
-                  ),
-                ),
-              ],
-            ),
-            Column(
-              children: [
-                Text(
-                  myProvider.availableBlocksCount.toString(),
-                  style: TextStyle(
-                    fontSize: 20,
-                    fontWeight: FontWeight.w600,
-                    color: Colors.green,
-                  ),
-                ),
-              ],
-            ),
-          ],
-        ),
-      ],
-    );
-  }
-
-  static TextButton buildQuitGameButton(Data myProvider) {
-    return TextButton(
-      child: Image(
-        image: AssetImage('assets/icons/button_back.png'),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => GameUtils.quitGame(myProvider),
-    );
-  }
-
-  static Container buildEndGameMessage(Data myProvider) {
-    String decorationImageAssetName = 'assets/icons/game_fail.png';
-
-    Widget decorationWidget = TextButton(
-      child: Image(
-        image: AssetImage(decorationImageAssetName),
-        fit: BoxFit.fill,
-      ),
-      onPressed: () => null,
-    );
-
-    return Container(
-      margin: EdgeInsets.all(2),
-      padding: EdgeInsets.all(2),
-      child: Table(
-        defaultColumnWidth: IntrinsicColumnWidth(),
-        children: [
-          TableRow(
-            children: [
-              Column(
-                children: [decorationWidget],
-              ),
-              Column(
-                children: [buildQuitGameButton(myProvider)],
-              ),
-              Column(
-                children: [decorationWidget],
-              ),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart
deleted file mode 100644
index c1f39ad01c7e0b8fc623d2e6e362ded3f0b39fde..0000000000000000000000000000000000000000
--- a/lib/layout/parameters.dart
+++ /dev/null
@@ -1,184 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/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 Container buildParametersSelector(Data myProvider) {
-    List<Widget> lines = [];
-
-    List parameters = myProvider.availableParameters;
-    for (var 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 Container(
-      child: 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 Container buildImageContainerWidget(String imageAssetCode) {
-    return Container(
-      child: buildImageWidget(imageAssetCode),
-    );
-  }
-
-  static Column buildDecorationImageWidget() {
-    return Column(
-      children: [
-        TextButton(
-          child: buildImageContainerWidget('placeholder'),
-          onPressed: () => null,
-        ),
-      ],
-    );
-  }
-
-  static Container buildStartNewGameButton(Data myProvider) {
-    return Container(
-      margin: EdgeInsets.all(blockMargin),
-      padding: EdgeInsets.all(blockPadding),
-      child: Table(
-        defaultColumnWidth: IntrinsicColumnWidth(),
-        children: [
-          TableRow(
-            children: [
-              buildDecorationImageWidget(),
-              Column(
-                children: [
-                  TextButton(
-                    child: buildImageContainerWidget('button_start'),
-                    onPressed: () => GameUtils.startNewGame(myProvider),
-                  ),
-                ],
-              ),
-              buildDecorationImageWidget(),
-            ],
-          ),
-        ],
-      ),
-    );
-  }
-
-  static Container buildResumeGameButton(Data myProvider) {
-    return Container(
-      margin: EdgeInsets.all(blockMargin),
-      padding: EdgeInsets.all(blockPadding),
-      child: Table(
-        defaultColumnWidth: 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 availableValues = myProvider.getParameterAvailableValues(parameterCode);
-
-    if (availableValues.length == 1) {
-      return SizedBox(height: 0.0);
-    }
-
-    return Table(
-      defaultColumnWidth: IntrinsicColumnWidth(),
-      children: [
-        TableRow(
-          children: [
-            for (var 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 683f95f0d7a9eabae68944ae5f366701c50cf32f..df1d2ede98d81dc85c3f60fd54d0c5f96c48fd82 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,36 +1,62 @@
+import 'dart:io';
+
+import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/screens/home.dart';
-import 'package:provider/provider.dart';
-import 'package:overlay_support/overlay_support.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hive/hive.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+import 'package:path_provider/path_provider.dart';
+
+import 'package:jeweled/config/theme.dart';
+import 'package:jeweled/cubit/settings_cubit.dart';
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/ui/skeleton.dart';
 
-void main() {
+void main() async {
+  /// Initialize packages
   WidgetsFlutterBinding.ensureInitialized();
-  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
-      .then((value) => runApp(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 {
+  const MyApp({super.key});
+
   @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: Home(),
-            routes: {
-              Home.id: (context) => Home(),
-            },
-          ),
-        );
-      }),
+    return MultiBlocProvider(
+      providers: [
+        BlocProvider<GameCubit>(create: (context) => GameCubit()),
+        BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
+      ],
+      child: MaterialApp(
+        title: 'Jeweled',
+        theme: appTheme,
+        home: const SkeletonScreen(),
+
+        // Localization stuff
+        localizationsDelegates: context.localizationDelegates,
+        supportedLocales: context.supportedLocales,
+        locale: context.locale,
+        debugShowCheckedModeBanner: false,
+      ),
     );
   }
 }
diff --git a/lib/models/cell_location.dart b/lib/models/cell_location.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b7a70ffd791c385899cb582eabcc664bd163ad74
--- /dev/null
+++ b/lib/models/cell_location.dart
@@ -0,0 +1,13 @@
+class CellLocation {
+  final int col;
+  final int row;
+
+  CellLocation({
+    required this.col,
+    required this.row,
+  });
+
+  factory CellLocation.go(int row, int col) {
+    return new CellLocation(col: col, row: row);
+  }
+}
diff --git a/lib/models/game.dart b/lib/models/game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..90426df03f00d80d8082dcdb98f95c868328dd3d
--- /dev/null
+++ b/lib/models/game.dart
@@ -0,0 +1,266 @@
+import 'dart:math';
+
+import 'package:jeweled/models/game_board.dart';
+import 'package:jeweled/models/game_cell.dart';
+import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/models/game_settings.dart';
+
+class Game {
+  final GameBoard board;
+  final GameSettings settings;
+  bool isRunning = false;
+  bool isFinished = false;
+  int availableBlocksCount = 0;
+  int movesCount = 0;
+  int score = 0;
+
+  Game({
+    required this.board,
+    required this.settings,
+    this.isRunning = false,
+    this.isFinished = false,
+    this.availableBlocksCount = 0,
+    this.movesCount = 0,
+    this.score = 0,
+  });
+
+  factory Game.createNull() {
+    return Game(
+      board: GameBoard.createNull(),
+      settings: GameSettings.createDefault(),
+    );
+  }
+
+  factory Game.createNew({GameSettings? gameSettings}) {
+    GameSettings settings = gameSettings ?? GameSettings.createDefault();
+
+    return Game(
+      board: GameBoard.createRandom(settings),
+      settings: settings,
+      isRunning: true,
+    );
+  }
+
+  GameCell getCell(CellLocation cellLocation) {
+    return this.board.cells[cellLocation.row][cellLocation.col];
+  }
+
+  int? getCellValue(CellLocation cellLocation) {
+    return this.getCell(cellLocation).value;
+  }
+
+  void updateCellValue(CellLocation locationToUpdate, int? value) {
+    this.board.cells[locationToUpdate.row][locationToUpdate.col].value = value;
+  }
+
+  void increaseMovesCount() {
+    this.movesCount += 1;
+  }
+
+  void increaseScore(int? count) {
+    this.score += (count ?? 0);
+  }
+
+  void updateAvailableBlocksCount() {
+    this.availableBlocksCount = this.getAvailableBlocks(this).length;
+  }
+
+  void updateGameIsRunning(bool gameIsRunning) {
+    this.isRunning = gameIsRunning;
+  }
+
+  void updateGameIsFinished(bool gameIsFinished) {
+    this.isFinished = gameIsFinished;
+  }
+
+  List<CellLocation> getSiblingCells(
+    final CellLocation referenceCellLocation,
+    List<CellLocation> siblingCells,
+  ) {
+    final int boardSizeHorizontal = this.settings.boardSize;
+    final int boardSizeVertical = this.settings.boardSize;
+
+    final int? referenceValue = this.getCellValue(referenceCellLocation);
+
+    for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
+      for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
+        if (deltaCol == 0 || deltaRow == 0) {
+          final int candidateRow = referenceCellLocation.row + deltaRow;
+          final int candidateCol = referenceCellLocation.col + deltaCol;
+
+          if ((candidateRow >= 0 && candidateRow < boardSizeVertical) &&
+              (candidateCol >= 0 && candidateCol < boardSizeHorizontal)) {
+            final candidateLocation = CellLocation.go(candidateRow, candidateCol);
+
+            if (this.getCellValue(candidateLocation) == referenceValue) {
+              bool alreadyFound = false;
+              for (var index = 0; index < siblingCells.length; index++) {
+                if ((siblingCells[index].row == candidateRow) &&
+                    (siblingCells[index].col == candidateCol)) {
+                  alreadyFound = true;
+                }
+              }
+              if (!alreadyFound) {
+                siblingCells.add(candidateLocation);
+                siblingCells = getSiblingCells(candidateLocation, siblingCells);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return siblingCells;
+  }
+
+  List<List<CellLocation>> getAvailableBlocks(final Game game) {
+    final int boardSizeHorizontal = game.settings.boardSize;
+    final int boardSizeVertical = game.settings.boardSize;
+
+    final List<List<CellLocation>> blocks = [];
+
+    for (var row = 0; row < boardSizeVertical; row++) {
+      for (var 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
+          bool alreadyFound = false;
+
+          blocks.forEach((List<CellLocation> foundBlock) {
+            foundBlock.forEach((CellLocation foundBlockCell) {
+              if ((foundBlockCell.row == row) && (foundBlockCell.col == col)) {
+                alreadyFound = true;
+              }
+            });
+          });
+          if (!alreadyFound) {
+            final List<CellLocation> block = game.getSiblingCells(cellLocation, []);
+            if (block.length >= 3) {
+              blocks.add(block);
+            }
+          }
+        }
+      }
+    }
+
+    return blocks;
+  }
+
+  bool hasAtLeastOneAvailableBlock() {
+    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++) {
+        final CellLocation cellLocation = CellLocation.go(row, col);
+        if (this.getCellValue(cellLocation) != null) {
+          final List<CellLocation> block = this.getSiblingCells(cellLocation, []);
+          if (block.length >= 3) {
+            // found one block => ok, not locked
+            return true;
+          }
+        }
+      }
+    }
+
+    print('Board is locked!');
+    return false;
+  }
+
+  bool isInBoard(CellLocation cell) {
+    if (cell.row > 0 &&
+        cell.row < this.settings.boardSize &&
+        cell.col > 0 &&
+        cell.col < this.settings.boardSize) {
+      return true;
+    }
+    return false;
+  }
+
+  int getFillValue(CellLocation referenceCellLocation) {
+    final int row = referenceCellLocation.row;
+    final int col = referenceCellLocation.col;
+
+    // build a list of values to pick one
+    final List<int> values = [];
+
+    // All eligible values
+    final int maxValue = this.settings.colorsCount;
+    for (int i = 1; i <= maxValue; i++) {
+      values.add(i);
+    }
+
+    // Add values of current col
+    for (int r = 0; r <= this.settings.boardSize; r++) {
+      if (this.isInBoard(CellLocation.go(r, col))) {
+        final int? value = this.getCellValue(CellLocation.go(r, col));
+        if (value != null) {
+          values.add(value);
+        }
+      }
+    }
+
+    // Add values of sibling cols
+    for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
+      final int c = col + deltaCol;
+      for (int r = 0; r < this.settings.boardSize; r++) {
+        if (this.isInBoard(CellLocation.go(r, c))) {
+          final int? value = this.getCellValue(CellLocation.go(r, c));
+          if (value != null) {
+            values.add(value);
+          }
+        }
+      }
+    }
+
+    // Add values of sibling cells
+    for (int deltaCol = -2; deltaCol <= 2; deltaCol++) {
+      final int c = col + deltaCol;
+      for (int deltaRow = -2; deltaRow <= 2; deltaRow++) {
+        final int r = row + deltaRow;
+        if (this.isInBoard(CellLocation.go(r, c))) {
+          final int? value = this.getCellValue(CellLocation.go(r, c));
+          if (value != null) {
+            values.add(value);
+          }
+        }
+      }
+    }
+
+    // Pick random value from "ponderated" list
+    return values[Random().nextInt(values.length)];
+  }
+
+  void dump() {
+    print('');
+    print('## Current game dump:');
+    print('');
+    this.settings.dump();
+    print('');
+    this.board.dump();
+    print('');
+    print('Game: ');
+    print('  isRunning: ' + isRunning.toString());
+    print('  isFinished: ' + isFinished.toString());
+    print('  movesCount: ' + movesCount.toString());
+    print('  score: ' + score.toString());
+    print('  availableBlocksCount: ' + availableBlocksCount.toString());
+    print('');
+  }
+
+  String toString() {
+    return 'Game(' + this.toJson().toString() + ')';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      'board': this.board.toJson(),
+      'settings': this.settings.toJson(),
+      'isRunning': this.isRunning,
+      'isFinished': this.isFinished,
+      'availableBlocksCount': this.availableBlocksCount,
+      'movesCount': this.movesCount,
+      'score': this.score,
+    };
+  }
+}
diff --git a/lib/models/game_board.dart b/lib/models/game_board.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bea21887bc25242d69c813194674ac15d1b8092e
--- /dev/null
+++ b/lib/models/game_board.dart
@@ -0,0 +1,70 @@
+import 'dart:math';
+
+import 'package:jeweled/models/game_cell.dart';
+import 'package:jeweled/models/game_settings.dart';
+
+class GameBoard {
+  final List<List<GameCell>> cells;
+
+  GameBoard({
+    required this.cells,
+  });
+
+  factory GameBoard.createNull() {
+    return GameBoard(cells: []);
+  }
+
+  factory GameBoard.createRandom(GameSettings gameSettings) {
+    final int boardSizeHorizontal = gameSettings.boardSize;
+    final int boardSizeVertical = gameSettings.boardSize;
+    final int maxValue = gameSettings.colorsCount;
+
+    final rand = new Random();
+
+    List<List<GameCell>> cells = [];
+    for (var rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
+      List<GameCell> row = [];
+      for (var colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
+        int value = 1 + rand.nextInt(maxValue);
+        row.add(GameCell(value));
+      }
+      cells.add(row);
+    }
+
+    return GameBoard(
+      cells: cells,
+    );
+  }
+
+  void dump() {
+    String horizontalRule = '----';
+    cells[0].forEach((element) {
+      horizontalRule += '-';
+    });
+
+    print('Board:');
+    print(horizontalRule);
+
+    for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
+      String row = '| ';
+      for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
+        row += cells[rowIndex][colIndex].value.toString();
+      }
+      row += ' |';
+
+      print(row);
+    }
+
+    print(horizontalRule);
+  }
+
+  String toString() {
+    return 'Board(' + this.toJson().toString() + ')';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      'cells': this.cells.toString(),
+    };
+  }
+}
diff --git a/lib/models/game_cell.dart b/lib/models/game_cell.dart
new file mode 100644
index 0000000000000000000000000000000000000000..25667ccf76bfbd4ef65f8651c120a7dbaab641b8
--- /dev/null
+++ b/lib/models/game_cell.dart
@@ -0,0 +1,17 @@
+class GameCell {
+  int? value;
+
+  GameCell(
+    this.value,
+  );
+
+  String toString() {
+    return 'Cell(' + this.toJson().toString() + ')';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      'value': this.value,
+    };
+  }
+}
diff --git a/lib/models/game_settings.dart b/lib/models/game_settings.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6e2cb12098401e1187ff7d5afc89928d9cf8a4be
--- /dev/null
+++ b/lib/models/game_settings.dart
@@ -0,0 +1,51 @@
+import 'package:jeweled/config/default_game_settings.dart';
+
+class GameSettings {
+  final int boardSize;
+  final int colorsCount;
+
+  GameSettings({
+    required this.boardSize,
+    required this.colorsCount,
+  });
+
+  static int getBoardSizeValueFromUnsafe(int size) {
+    if (DefaultGameSettings.allowedBoardSizeValues.contains(size)) {
+      return size;
+    }
+
+    return DefaultGameSettings.defaultBoardSizeValue;
+  }
+
+  static int getColorsCountValueFromUnsafe(int colorsCount) {
+    if (DefaultGameSettings.allowedColorsCountValues.contains(colorsCount)) {
+      return colorsCount;
+    }
+
+    return DefaultGameSettings.defaultColorsCountValue;
+  }
+
+  factory GameSettings.createDefault() {
+    return GameSettings(
+      boardSize: DefaultGameSettings.defaultBoardSizeValue,
+      colorsCount: DefaultGameSettings.defaultColorsCountValue,
+    );
+  }
+
+  void dump() {
+    print('Settings: ');
+    print('  boardSize: ' + boardSize.toString());
+    print('  colorsCount: ' + colorsCount.toString());
+  }
+
+  String toString() {
+    return 'GameSettings(' + this.toJson().toString() + ')';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{
+      'boardSize': this.boardSize,
+      'colorsCount': this.colorsCount,
+    };
+  }
+}
diff --git a/lib/provider/data.dart b/lib/provider/data.dart
deleted file mode 100644
index c07515902c44a6bfe3bed62ec778cdf58e389cd1..0000000000000000000000000000000000000000
--- a/lib/provider/data.dart
+++ /dev/null
@@ -1,320 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/foundation.dart';
-import 'package:jeweled_game/entities/cell.dart';
-import 'package:shared_preferences/shared_preferences.dart';
-
-class Data extends ChangeNotifier {
-  // Configuration available parameters
-  List _availableParameters = ['level', 'size', 'colors', 'skin'];
-
-  List _availableLevelValues = ['easy', 'medium', 'hard', 'nightmare'];
-  List _availableSizeValues = ['small', 'medium', 'large', 'extra'];
-  List _availableColorsValues = ['5', '6', '7', '8'];
-  List _availableSkinValues = ['default'];
-
-  List get availableParameters => _availableParameters;
-  List get availableLevelValues => _availableLevelValues;
-  List get availableSizeValues => _availableSizeValues;
-  List get availableSkinValues => _availableSkinValues;
-
-  // Application default configuration
-  String _parameterLevel = '';
-  String _parameterLevelDefault = 'medium';
-  String _parameterSize = '';
-  String _parameterSizeDefault = 'medium';
-  String _parameterColors = '';
-  String _parameterColorsDefault = '6';
-  String _parameterSkin = '';
-  String _parameterSkinDefault = 'default';
-
-  // Application current configuration
-  String get parameterLevel => _parameterLevel;
-  String get parameterSize => _parameterSize;
-  String get parameterSkin => _parameterSkin;
-
-  // Game data
-  bool _gameIsRunning = false;
-  bool _gameIsFinished = false;
-  int _sizeVertical = 0;
-  int _sizeHorizontal = 0;
-  int _colorsCount = 0;
-  List<List<Cell>> _cells = [];
-  String _currentState = '';
-  int _availableBlocksCount = 0;
-  int _score = 0;
-  int _movesCount = 0;
-
-  void updateParameterLevel(String parameterLevel) {
-    _parameterLevel = parameterLevel;
-    notifyListeners();
-  }
-
-  int get sizeVertical => _sizeVertical;
-  int get sizeHorizontal => _sizeHorizontal;
-  void updateParameterSize(String parameterSize) {
-    _parameterSize = parameterSize;
-    updateBoardSize(getBoardSizeFromParameter(parameterSize));
-    notifyListeners();
-  }
-
-  void updateParameterColors(String parameterColors) {
-    _parameterColors = parameterColors;
-    updateColorsCount(getColorsCountFromParameter(parameterColors));
-    notifyListeners();
-  }
-
-  void updateParameterSkin(String parameterSkin) {
-    _parameterSkin = parameterSkin;
-    notifyListeners();
-  }
-
-  String getParameterValue(String parameterCode) {
-    switch (parameterCode) {
-      case 'level':
-        {
-          return _parameterLevel;
-        }
-      case 'size':
-        {
-          return _parameterSize;
-        }
-      case 'colors':
-        {
-          return _parameterColors;
-        }
-      case 'skin':
-        {
-          return _parameterSkin;
-        }
-    }
-    return '';
-  }
-
-  List getParameterAvailableValues(String parameterCode) {
-    switch (parameterCode) {
-      case 'level':
-        {
-          return _availableLevelValues;
-        }
-      case 'size':
-        {
-          return _availableSizeValues;
-        }
-      case 'colors':
-        {
-          return _availableColorsValues;
-        }
-      case 'skin':
-        {
-          return _availableSkinValues;
-        }
-    }
-    return [];
-  }
-
-  void setParameterValue(String parameterCode, String parameterValue) async {
-    switch (parameterCode) {
-      case 'level':
-        {
-          updateParameterLevel(parameterValue);
-        }
-        break;
-      case 'size':
-        {
-          updateParameterSize(parameterValue);
-        }
-        break;
-      case 'colors':
-        {
-          updateParameterColors(parameterValue);
-        }
-        break;
-      case 'skin':
-        {
-          updateParameterSkin(parameterValue);
-        }
-        break;
-    }
-    final prefs = await SharedPreferences.getInstance();
-    prefs.setString(parameterCode, parameterValue);
-  }
-
-  void initParametersValues() async {
-    final prefs = await SharedPreferences.getInstance();
-    setParameterValue('level', prefs.getString('level') ?? _parameterLevelDefault);
-    setParameterValue('size', prefs.getString('size') ?? _parameterSizeDefault);
-    setParameterValue('colors', prefs.getString('colors') ?? _parameterColorsDefault);
-    setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault);
-  }
-
-  String getBoardSizeFromParameter(String parameterSize) {
-    switch (parameterSize) {
-      case 'small':
-        {
-          return '6x6';
-        }
-      case 'medium':
-        {
-          return '10x10';
-        }
-      case 'large':
-        {
-          return '14x14';
-        }
-      case 'extra':
-        {
-          return '20x20';
-        }
-    }
-    return getBoardSizeFromParameter(_parameterSizeDefault);
-  }
-
-  int getColorsCountFromParameter(String parameterColors) {
-    switch (parameterColors) {
-      case '5':
-        {
-          return 5;
-        }
-      case '6':
-        {
-          return 6;
-        }
-      case '7':
-        {
-          return 7;
-        }
-      case '8':
-        {
-          return 8;
-        }
-    }
-    return getColorsCountFromParameter(_parameterColorsDefault);
-  }
-
-  void updateBoardSize(String boardSize) {
-    _sizeHorizontal = int.parse(boardSize.split('x')[0]);
-    _sizeVertical = int.parse(boardSize.split('x')[1]);
-  }
-
-  int get colorsCount => _colorsCount;
-  void updateColorsCount(int colorsCount) {
-    _colorsCount = colorsCount;
-  }
-
-  String get currentState => _currentState;
-
-  String computeCurrentGameState() {
-    String cellsValues = '';
-    for (var rowIndex = 0; rowIndex < _cells.length; rowIndex++) {
-      for (var colIndex = 0; colIndex < _cells[rowIndex].length; colIndex++) {
-        cellsValues += _cells[rowIndex][colIndex].value;
-      }
-    }
-
-    var currentState = {
-      'level': _parameterLevel,
-      'size': _parameterSize,
-      'skin': _parameterSkin,
-      'board': cellsValues,
-    };
-
-    return json.encode(currentState);
-  }
-
-  void saveCurrentGameState() async {
-    if (_gameIsRunning) {
-      _currentState = computeCurrentGameState();
-
-      final prefs = await SharedPreferences.getInstance();
-      prefs.setString('savedState', _currentState);
-    } else {
-      resetCurrentSavedState();
-    }
-  }
-
-  void resetCurrentSavedState() async {
-    _currentState = '';
-
-    final prefs = await SharedPreferences.getInstance();
-    prefs.setString('savedState', _currentState);
-    notifyListeners();
-  }
-
-  void loadCurrentSavedState() async {
-    final prefs = await SharedPreferences.getInstance();
-    _currentState = prefs.getString('savedState') ?? '';
-  }
-
-  bool hasCurrentSavedState() {
-    return (_currentState != '');
-  }
-
-  Map<String, dynamic> getCurrentSavedState() {
-    if (_currentState != '') {
-      Map<String, dynamic> savedState = json.decode(_currentState);
-      if (savedState.isNotEmpty) {
-        return savedState;
-      }
-    }
-    return {};
-  }
-
-  List<List<Cell>> get cells => _cells;
-  void updateCells(List<List<Cell>> cells) {
-    _cells = cells;
-    notifyListeners();
-  }
-
-  Cell getCell(int row, int col) {
-    return cells[row][col];
-  }
-
-  String getCellValue(int row, int col) {
-    return getCell(row, col).value;
-  }
-
-  updateCellValue(int col, int row, String value) {
-    _cells[row][col].value = value;
-    notifyListeners();
-  }
-
-  bool get isGameRunning => _gameIsRunning;
-  void updateGameIsRunning(bool gameIsRunning) {
-    _gameIsRunning = gameIsRunning;
-    notifyListeners();
-  }
-
-  bool get isGameFinished => _gameIsFinished;
-  void updateGameIsFinished(bool gameIsFinished) {
-    _gameIsFinished = gameIsFinished;
-    notifyListeners();
-  }
-
-  int get availableBlocksCount => _availableBlocksCount;
-  void updateAvailableBlocksCount(int availableBlocksCount) {
-    _availableBlocksCount = availableBlocksCount;
-    notifyListeners();
-  }
-
-  int get score => _score;
-  void increaseScore(int increment) {
-    _score += increment;
-    notifyListeners();
-  }
-
-  int get movesCount => _movesCount;
-  void increaseMovesCount() {
-    _movesCount++;
-    notifyListeners();
-  }
-
-  void resetGame() {
-    _gameIsRunning = false;
-    _gameIsFinished = false;
-    _availableBlocksCount = 0;
-    _score = 0;
-    _movesCount = 0;
-    notifyListeners();
-  }
-}
diff --git a/lib/screens/home.dart b/lib/screens/home.dart
deleted file mode 100644
index 6db733efe8f6c85a698d18edd50483590afdd585..0000000000000000000000000000000000000000
--- a/lib/screens/home.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/layout/game.dart';
-import 'package:jeweled_game/layout/parameters.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/utils/game_utils.dart';
-import 'package:provider/provider.dart';
-import 'package:overlay_support/overlay_support.dart';
-
-class Home extends StatefulWidget {
-  static const String id = 'home';
-
-  @override
-  _HomeState createState() => _HomeState();
-}
-
-class _HomeState extends State<Home> {
-  @override
-  void initState() {
-    super.initState();
-
-    Data myProvider = Provider.of<Data>(context, listen: false);
-    myProvider.initParametersValues();
-    myProvider.loadCurrentSavedState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    Data myProvider = Provider.of<Data>(context);
-    double boardWidth = MediaQuery.of(context).size.width;
-
-    List<Widget> menuActions = [];
-
-    if (myProvider.isGameRunning) {
-      menuActions = [
-        TextButton(
-          child: Container(
-            decoration: BoxDecoration(
-              borderRadius: BorderRadius.circular(4),
-              border: Border.all(
-                color: Colors.blue,
-                width: 4,
-              ),
-            ),
-            child: 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.isGameRunning
-              ? Game.buildGameWidget(myProvider, boardWidth)
-              : Parameters.buildParametersSelector(myProvider),
-        ),
-      ),
-    );
-  }
-}
diff --git a/lib/ui/painters/game_board_painter.dart b/lib/ui/painters/game_board_painter.dart
new file mode 100644
index 0000000000000000000000000000000000000000..08cad440ef153547368e0ec7ac1a1c5e01513137
--- /dev/null
+++ b/lib/ui/painters/game_board_painter.dart
@@ -0,0 +1,121 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+
+import 'package:jeweled/models/game_cell.dart';
+import 'package:jeweled/models/game.dart';
+import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/utils/color_theme.dart';
+
+class GameBoardPainter extends CustomPainter {
+  const GameBoardPainter(this.currentGame);
+
+  final Game currentGame;
+
+  static const drawTextValue = false;
+
+  @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);
+
+    // 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 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));
+        }
+      }
+    }
+
+    // borders
+    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++) {
+        final double x = cellSize * col;
+
+        final GameCell cell = cells[row][col];
+        final int? cellValue = 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);
+        }
+
+        // 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);
+        }
+
+        // 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);
+        }
+
+        // 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);
+        }
+      }
+    }
+  }
+
+  @override
+  bool shouldRepaint(CustomPainter oldDelegate) {
+    return false;
+  }
+}
diff --git a/lib/ui/screens/game.dart b/lib/ui/screens/game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..88777784bdce9367b3871150f57a79f017e860ce
--- /dev/null
+++ b/lib/ui/screens/game.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/ui/widgets/game.dart';
+import 'package:jeweled/ui/widgets/parameters.dart';
+
+class ScreenGame extends StatelessWidget {
+  const ScreenGame({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Material(
+      color: Theme.of(context).colorScheme.background,
+      child: Column(
+        children: <Widget>[
+          const SizedBox(height: 8),
+          BlocBuilder<GameCubit, GameState>(
+            builder: (BuildContext context, GameState gameState) {
+              return gameState.currentGame.isRunning ? const GameWidget() : const Parameters();
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6b24bb4c334f0dbb526ffb75ee19dfc788d81420
--- /dev/null
+++ b/lib/ui/skeleton.dart
@@ -0,0 +1,18 @@
+import 'package:flutter/material.dart';
+
+import 'package:jeweled/ui/screens/game.dart';
+import 'package:jeweled/ui/widgets/global_app_bar.dart';
+
+class SkeletonScreen extends StatelessWidget {
+  const SkeletonScreen({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: GlobalAppBar(),
+      extendBodyBehindAppBar: false,
+      body: ScreenGame(),
+      backgroundColor: Theme.of(context).colorScheme.background,
+    );
+  }
+}
diff --git a/lib/ui/widgets/app_titles.dart b/lib/ui/widgets/app_titles.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7cbbb2030419047b3dcf093a2195a498bd8e8ce9
--- /dev/null
+++ b/lib/ui/widgets/app_titles.dart
@@ -0,0 +1,17 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class AppTitle extends StatelessWidget {
+  const AppTitle({super.key, required this.text});
+
+  final String text;
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      tr(text),
+      textAlign: TextAlign.start,
+      style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game.dart b/lib/ui/widgets/game.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3170f63f912fb6b0a8dd03ec3a62dc59fe75a5a9
--- /dev/null
+++ b/lib/ui/widgets/game.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/models/game.dart';
+import 'package:jeweled/ui/widgets/game_board.dart';
+import 'package:jeweled/ui/widgets/game_bottom_buttons.dart';
+import 'package:jeweled/ui/widgets/game_top_indicator.dart';
+
+class GameWidget extends StatelessWidget {
+  const GameWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      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(),
+          ],
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/game_board.dart b/lib/ui/widgets/game_board.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ab9265d89728819612587a86e890975cbc4996f0
--- /dev/null
+++ b/lib/ui/widgets/game_board.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/models/game.dart';
+import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/ui/painters/game_board_painter.dart';
+
+class GameBoard extends StatelessWidget {
+  const GameBoard({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final double displayWidth = MediaQuery.of(context).size.width;
+
+    return Center(
+      child: BlocBuilder<GameCubit, GameState>(
+        builder: (BuildContext context, GameState gameState) {
+          final Game currentGame = gameState.currentGame;
+
+          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 GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
+              gameCubit.tapOnCell(CellLocation.go(row, col));
+            },
+            child: CustomPaint(
+              size: Size(displayWidth, displayWidth),
+              willChange: false,
+              painter: GameBoardPainter(currentGame),
+              isComplex: true,
+            ),
+          );
+        },
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game_bottom_buttons.dart b/lib/ui/widgets/game_bottom_buttons.dart
new file mode 100644
index 0000000000000000000000000000000000000000..684279ec3a2f7cd3e33002ab86397109049021d6
--- /dev/null
+++ b/lib/ui/widgets/game_bottom_buttons.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+
+class GameBottomButtonsWidget extends StatelessWidget {
+  const GameBottomButtonsWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    String decorationImageAssetName = 'assets/icons/game_fail.png';
+
+    Widget decorationWidget = TextButton(
+      child: Image(
+        image: AssetImage(decorationImageAssetName),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () => null,
+    );
+
+    return Container(
+      child: Table(
+        defaultColumnWidth: IntrinsicColumnWidth(),
+        children: [
+          TableRow(
+            children: [
+              Column(
+                children: [decorationWidget],
+              ),
+              Column(
+                children: [
+                  TextButton(
+                    child: Image(
+                      image: AssetImage('assets/icons/button_back.png'),
+                      fit: BoxFit.fill,
+                    ),
+                    onPressed: () {
+                      final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
+                      gameCubit.quitGame();
+                    },
+                  )
+                ],
+              ),
+              Column(
+                children: [decorationWidget],
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game_top_indicator.dart b/lib/ui/widgets/game_top_indicator.dart
new file mode 100644
index 0000000000000000000000000000000000000000..298c502d72b5230038b4e03a81617744f27d288a
--- /dev/null
+++ b/lib/ui/widgets/game_top_indicator.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/models/game.dart';
+
+class GameTopIndicatorWidget extends StatelessWidget {
+  const GameTopIndicatorWidget({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    const Color scoreTextColor = Colors.white;
+    const Color movesCountTextColor = Colors.grey;
+    const Color availableBlocksCountTextColor = Colors.green;
+
+    const double scoreFontSize = 40;
+    const double movesCountFontSize = 15;
+    const double availableBlocksCountFontSize = 20;
+
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        return Table(
+          children: [
+            TableRow(
+              children: [
+                Column(
+                  children: [
+                    Text(
+                      currentGame.score.toString(),
+                      style: const TextStyle(
+                        fontSize: scoreFontSize,
+                        fontWeight: FontWeight.w600,
+                        color: scoreTextColor,
+                      ),
+                    ),
+                    Text(
+                      currentGame.movesCount.toString(),
+                      style: const TextStyle(
+                        fontSize: movesCountFontSize,
+                        fontWeight: FontWeight.w600,
+                        color: movesCountTextColor,
+                      ),
+                    ),
+                  ],
+                ),
+                Column(
+                  children: [
+                    Text(
+                      currentGame.availableBlocksCount.toString(),
+                      style: const TextStyle(
+                        fontSize: availableBlocksCountFontSize,
+                        fontWeight: FontWeight.w600,
+                        color: availableBlocksCountTextColor,
+                      ),
+                    ),
+                  ],
+                ),
+              ],
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart
new file mode 100644
index 0000000000000000000000000000000000000000..afcf1c9304ebc53b9758b5f91698f93fbdc8a517
--- /dev/null
+++ b/lib/ui/widgets/global_app_bar.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/models/game.dart';
+import 'package:jeweled/ui/widgets/app_titles.dart';
+
+class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
+  const GlobalAppBar({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        final List<Widget> menuActions = [];
+
+        if (currentGame.isRunning) {
+          menuActions.add(TextButton(
+            child: Container(
+              child: Image(
+                image: AssetImage('assets/icons/button_back.png'),
+                fit: BoxFit.fill,
+              ),
+            ),
+            onPressed: () => null,
+            onLongPress: () {
+              final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
+              gameCubit.quitGame();
+            },
+          ));
+        }
+
+        return AppBar(
+          title: const AppTitle(text: 'app_name'),
+          actions: menuActions,
+        );
+      },
+    );
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(50);
+}
diff --git a/lib/ui/widgets/parameters.dart b/lib/ui/widgets/parameters.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6ee9a475c856ea78eddffd6295ade21f9d255dd3
--- /dev/null
+++ b/lib/ui/widgets/parameters.dart
@@ -0,0 +1,142 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/config/default_game_settings.dart';
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/cubit/settings_cubit.dart';
+import 'package:jeweled/models/game_settings.dart';
+
+class Parameters extends StatelessWidget {
+  const Parameters({super.key});
+
+  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) {
+    return BlocBuilder<SettingsCubit, SettingsState>(
+      builder: (BuildContext context, SettingsState settingsState) {
+        final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
+        final SettingsCubit settingsCubit = BlocProvider.of<SettingsCubit>(context);
+
+        final List<Widget> lines = [];
+
+        DefaultGameSettings.availableParameters.forEach((code) {
+          final List<dynamic> availableValues = DefaultGameSettings.getAvailableValues(code);
+
+          if (availableValues.length > 1) {
+            final List<Widget> parameterButtons = [];
+
+            final dynamic currentValue = settingsCubit.getParameterValue(code);
+
+            availableValues.forEach((value) {
+              final bool isActive = (value == currentValue);
+              final String imageAsset = code + '_' + value.toString();
+
+              final Widget parameterButton = 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: () => settingsCubit.setParameterValue(code, value),
+              );
+
+              parameterButtons.add(parameterButton);
+            });
+
+            lines.add(Table(
+              defaultColumnWidth: IntrinsicColumnWidth(),
+              children: [
+                TableRow(
+                  children: parameterButtons,
+                ),
+              ],
+            ));
+
+            lines.add(SizedBox(height: separatorHeight));
+          }
+        });
+
+        return Container(
+          child: Column(
+            children: [
+              SizedBox(height: separatorHeight),
+              Column(
+                children: lines,
+              ),
+              SizedBox(height: separatorHeight),
+              buildStartNewGameButton(gameCubit, settingsState.settings),
+            ],
+          ),
+        );
+      },
+    );
+  }
+
+  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: () => null,
+        ),
+      ],
+    );
+  }
+
+  static Container buildStartNewGameButton(GameCubit gameCubit, GameSettings settings) {
+    return Container(
+      margin: EdgeInsets.all(blockMargin),
+      padding: EdgeInsets.all(blockPadding),
+      child: Table(
+        defaultColumnWidth: IntrinsicColumnWidth(),
+        children: [
+          TableRow(
+            children: [
+              buildDecorationImageWidget(),
+              Column(
+                children: [
+                  TextButton(
+                    child: buildImageContainerWidget('button_start'),
+                    onPressed: () => gameCubit.startNewGame(settings),
+                  ),
+                ],
+              ),
+              buildDecorationImageWidget(),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart
deleted file mode 100644
index afe0c62acc2bd6722b9881925bd514b2797a73e0..0000000000000000000000000000000000000000
--- a/lib/utils/board_utils.dart
+++ /dev/null
@@ -1,270 +0,0 @@
-import 'dart:math';
-
-import 'package:jeweled_game/entities/cell.dart';
-import 'package:jeweled_game/provider/data.dart';
-
-class BoardUtils {
-  static printGrid(List cells) {
-    print('');
-    print('-------');
-    for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
-      String row = '';
-      for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
-        row += cells[rowIndex][colIndex].value;
-      }
-      print(row);
-    }
-    print('-------');
-    print('');
-  }
-
-  static createNewBoard(Data myProvider) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-    int maxValue = myProvider.colorsCount;
-
-    var rand = new Random();
-
-    List<List<Cell>> grid = [];
-    for (var rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
-      List<Cell> row = [];
-      for (var colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
-        int value = 1 + rand.nextInt(maxValue);
-        row.add(Cell(value.toString()));
-      }
-      grid.add(row);
-    }
-    printGrid(grid);
-
-    myProvider.resetGame();
-    myProvider.updateCells(grid);
-    myProvider.updateAvailableBlocksCount(getAvailableBlocks(myProvider).length);
-  }
-
-  // static List createBoardFromSavedState(Data myProvider, String savedBoard) {
-  //   List<List<Cell?>> board = [];
-  //   int boardSize = pow((savedBoard.length / 6), 1 / 2).round();
-  //   String boardSizeAsString = boardSize.toString() + 'x' + boardSize.toString();
-  //   myProvider.updateParameterSize(boardSizeAsString);
-
-  //   int index = 0;
-  //   for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
-  //     List<Cell?> row = [];
-  //     for (var colIndex = 0; colIndex < boardSize; colIndex++) {
-  //       String value = savedBoard[index++];
-
-  //       Cell cell = Cell(value);
-
-  //       row.add(cell);
-  //     }
-  //     board.add(row);
-  //   }
-
-  //   printGrid(board);
-
-  //   return board;
-  // }
-
-  static List<List<int>> getSiblingCells(
-      Data myProvider, row, col, List<List<int>> siblingCells) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-
-    String referenceValue = myProvider.getCellValue(row, col);
-
-    for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
-      for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
-        if (deltaCol == 0 || deltaRow == 0) {
-          int candidateRow = row + deltaRow;
-          int candidateCol = col + deltaCol;
-
-          if ((candidateRow >= 0 && candidateRow < boardSizeVertical) &&
-              (candidateCol >= 0 && candidateCol < boardSizeHorizontal)) {
-            if (myProvider.getCellValue(candidateRow, candidateCol) == referenceValue) {
-              bool alreadyFound = false;
-              for (var index = 0; index < siblingCells.length; index++) {
-                if ((siblingCells[index][0] == candidateRow) &&
-                    (siblingCells[index][1] == candidateCol)) {
-                  alreadyFound = true;
-                }
-              }
-              if (!alreadyFound) {
-                siblingCells.add([candidateRow, candidateCol]);
-                siblingCells =
-                    getSiblingCells(myProvider, candidateRow, candidateCol, siblingCells);
-              }
-            }
-          }
-        }
-      }
-    }
-
-    return siblingCells;
-  }
-
-  static List getAvailableBlocks(Data myProvider) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-
-    List blocks = [];
-
-    for (var row = 0; row < boardSizeVertical; row++) {
-      for (var col = 0; col < boardSizeHorizontal; col++) {
-        if (myProvider.getCellValue(row, col) != '0') {
-          // if current cell not already in a found block
-          bool alreadyFound = false;
-          blocks.forEach((foundBlock) {
-            foundBlock.forEach((foundBlockCell) {
-              if ((foundBlockCell[0] == row) && (foundBlockCell[1] == col)) {
-                alreadyFound = true;
-              }
-            });
-          });
-          if (!alreadyFound) {
-            List<List<int>> block = getSiblingCells(myProvider, row, col, []);
-            if (block.length >= 3) {
-              blocks.add(block);
-            }
-          }
-        }
-      }
-    }
-
-    return blocks;
-  }
-
-  static bool checkBoardIsBlocked(Data myProvider) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-
-    for (var row = 0; row < boardSizeVertical; row++) {
-      for (var col = 0; col < boardSizeHorizontal; col++) {
-        if (myProvider.getCellValue(row, col) != '0') {
-          List<List<int>> block = getSiblingCells(myProvider, row, col, []);
-          if (block.length >= 3) {
-            // found one block => ok, not locked
-            return false;
-          }
-        }
-      }
-    }
-
-    print('Board is locked');
-    return true;
-  }
-
-  static bool isInBoard(Data myProvider, int row, int col) {
-    if (row > 0 &&
-        row < myProvider.sizeHorizontal &&
-        col > 0 &&
-        col < myProvider.sizeVertical) {
-      return true;
-    }
-    return false;
-  }
-
-  static String getFillValue(Data myProvider, int row, int col) {
-    // build a list of values to pick one
-    List<String> values = [];
-
-    // All eligible values
-    int maxValue = myProvider.colorsCount;
-    for (int i = 1; i <= maxValue; i++) {
-      values.add(i.toString());
-    }
-
-    // Add values of current col
-    for (int r = 0; r <= myProvider.sizeVertical; r++) {
-      if (isInBoard(myProvider, r, col)) {
-        String value = myProvider.getCellValue(r, col);
-        if (value != '0') {
-          values.add(value);
-        }
-      }
-    }
-
-    // Add values of sibling cols
-    for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
-      int c = col + deltaCol;
-      for (int r = 0; r < myProvider.sizeVertical; r++) {
-        if (isInBoard(myProvider, r, c)) {
-          String value = myProvider.getCellValue(r, c);
-          if (value != '0') {
-            values.add(value);
-          }
-        }
-      }
-    }
-
-    // Add values of sibling cells
-    for (int deltaCol = -2; deltaCol <= 2; deltaCol++) {
-      int c = col + deltaCol;
-      for (int deltaRow = -2; deltaRow <= 2; deltaRow++) {
-        int r = row + deltaRow;
-        if (isInBoard(myProvider, r, c)) {
-          String value = myProvider.getCellValue(r, c);
-          if (value != '0') {
-            values.add(value);
-          }
-        }
-      }
-    }
-
-    // Pick random value from "ponderated" list
-    return values[Random().nextInt(values.length)];
-  }
-
-  static moveCellsDown(Data myProvider) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-
-    for (int row = 0; row < boardSizeVertical; row++) {
-      for (int col = 0; col < boardSizeHorizontal; col++) {
-        if (myProvider.getCellValue(row, col) == '0') {
-          for (int r = row; r > 0; r--) {
-            myProvider.updateCellValue(col, r, myProvider.getCellValue(r - 1, col));
-          }
-          myProvider.updateCellValue(col, 0, getFillValue(myProvider, row, col));
-        }
-      }
-    }
-  }
-
-  static deleteCell(Data myProvider, int row, int col) {
-    myProvider.updateCellValue(col, row, '0');
-  }
-
-  static void deleteBlock(Data myProvider, List<List<int>> block) {
-    // Sort cells from top to bottom
-    block.sort((cell1, cell2) => cell1[0].compareTo(cell2[0]));
-    // Delete all cells
-    block.forEach((element) {
-      deleteCell(myProvider, element[0], element[1]);
-    });
-    // Gravity!
-    moveCellsDown(myProvider);
-  }
-
-  static int getScoreFromBlock(int blockSize) {
-    return 3 * (blockSize - 2);
-  }
-
-  static void tapOnCell(Data myProvider, int row, int col) {
-    String cellValue = myProvider.getCellValue(row, col);
-    print('Tap on cell[' + col.toString() + '][' + row.toString() + ']: ' + cellValue);
-
-    if (cellValue != '0') {
-      List<List<int>> block = getSiblingCells(myProvider, row, col, []);
-      if (block.length >= 3) {
-        deleteBlock(myProvider, block);
-        myProvider.increaseMovesCount();
-        myProvider.increaseScore(block.length);
-        myProvider.updateAvailableBlocksCount(getAvailableBlocks(myProvider).length);
-      }
-    }
-
-    if (checkBoardIsBlocked(myProvider)) {
-      myProvider.updateGameIsFinished(true);
-    }
-  }
-}
diff --git a/lib/utils/color_extensions.dart b/lib/utils/color_extensions.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4e55e338f0d3ed98b233d1ef887b7b3e17e29d97
--- /dev/null
+++ b/lib/utils/color_extensions.dart
@@ -0,0 +1,33 @@
+import 'dart:ui';
+
+extension ColorExtension on Color {
+  Color darken([int percent = 40]) {
+    assert(1 <= percent && percent <= 100);
+    final value = 1 - percent / 100;
+    return Color.fromARGB(
+      alpha,
+      (red * value).round(),
+      (green * value).round(),
+      (blue * value).round(),
+    );
+  }
+
+  Color lighten([int percent = 40]) {
+    assert(1 <= percent && percent <= 100);
+    final value = percent / 100;
+    return Color.fromARGB(
+      alpha,
+      (red + ((255 - red) * value)).round(),
+      (green + ((255 - green) * value)).round(),
+      (blue + ((255 - blue) * value)).round(),
+    );
+  }
+
+  Color avg(Color other) {
+    final red = (this.red + other.red) ~/ 2;
+    final green = (this.green + other.green) ~/ 2;
+    final blue = (this.blue + other.blue) ~/ 2;
+    final alpha = (this.alpha + other.alpha) ~/ 2;
+    return Color.fromARGB(alpha, red, green, blue);
+  }
+}
diff --git a/lib/utils/color_theme.dart b/lib/utils/color_theme.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2b10565a36d1f956202b2d30f13fb21bef8bd707
--- /dev/null
+++ b/lib/utils/color_theme.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+
+class ColorTheme {
+  static Map<String, List<int>> itemColors = {
+    'default': [
+      0xffffff,
+      0xe63a3f,
+      0x708cfd,
+      0x359c35,
+      0xffce2c,
+      0xff6f43,
+      0xa13cb1,
+      0x38ffff,
+      0xf2739d,
+    ],
+  };
+  static int defaultItemColor = 0x808080;
+
+  static int getColorCode(int? value) {
+    const skin = 'default';
+
+    if (value != null && itemColors.containsKey(skin) && null != itemColors[skin]) {
+      List<int> skinColors = itemColors[skin] ?? [];
+      if (skinColors.length > value) {
+        return (skinColors[value]) | 0xFF000000;
+      }
+    }
+    return defaultItemColor | 0xFF000000;
+  }
+
+  static Color getBorderColor() {
+    return Colors.black;
+  }
+}
diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart
deleted file mode 100644
index 3515d2fad48b19c56491eb47bcf233ce76174fe0..0000000000000000000000000000000000000000
--- a/lib/utils/game_utils.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/utils/board_utils.dart';
-// import 'package:jeweled_game/utils/board_utils.dart';
-
-class GameUtils {
-  static Future<void> quitGame(Data myProvider) async {
-    myProvider.updateGameIsRunning(false);
-  }
-
-  static Future<void> startNewGame(Data myProvider) async {
-    print('Starting game');
-    print('- level: ' + myProvider.parameterLevel);
-    print('- size: ' + myProvider.parameterSize);
-
-    myProvider.resetGame();
-    BoardUtils.createNewBoard(myProvider);
-
-    myProvider.updateGameIsRunning(true);
-  }
-
-  static void deleteSavedGame(Data myProvider) {
-    myProvider.resetCurrentSavedState();
-  }
-
-  static void resumeSavedGame(Data myProvider) {
-    Map<String, dynamic> savedState = myProvider.getCurrentSavedState();
-    if (savedState.isNotEmpty) {
-      try {
-        myProvider.setParameterValue('level', savedState['level']);
-        myProvider.setParameterValue('size', savedState['size']);
-        myProvider.setParameterValue('skin', savedState['skin']);
-
-        // myProvider.updateCells(
-        //     BoardUtils.createBoardFromSavedState(myProvider, savedState['board']));
-        myProvider.updateGameIsRunning(true);
-      } catch (e) {
-        print('Failed to resume game. Will start new one instead.');
-        myProvider.resetCurrentSavedState();
-        myProvider.initParametersValues();
-        startNewGame(myProvider);
-      }
-    } else {
-      myProvider.resetCurrentSavedState();
-      myProvider.initParametersValues();
-      startNewGame(myProvider);
-    }
-  }
-}
diff --git a/pubspec.lock b/pubspec.lock
index f78ae3bfbc5b91625f56452dcbc944e3e78db356..f0a711fa116acf1950fd1e05861e1a227e16cc00 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,14 +1,22 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
-  async:
+  args:
     dependency: transitive
     description:
-      name: async
-      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+      name: args
+      sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
       url: "https://pub.dev"
     source: hosted
-    version: "2.11.0"
+    version: "2.4.2"
+  bloc:
+    dependency: transitive
+    description:
+      name: bloc
+      sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49"
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.2"
   characters:
     dependency: transitive
     description:
@@ -17,14 +25,54 @@ 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:
       name: collection
-      sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
+      sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
       url: "https://pub.dev"
     source: hosted
-    version: "1.17.2"
+    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: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.3"
+  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:
@@ -37,20 +85,57 @@ packages:
     dependency: transitive
     description:
       name: file
-      sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
+      sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
       url: "https://pub.dev"
     source: hosted
-    version: "6.1.4"
+    version: "7.0.0"
   flutter:
     dependency: "direct main"
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_bloc:
+    dependency: "direct main"
+    description:
+      name: flutter_bloc
+      sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.3"
+  flutter_localizations:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   flutter_web_plugins:
     dependency: transitive
     description: flutter
     source: sdk
     version: "0.0.0"
+  hive:
+    dependency: transitive
+    description:
+      name: hive
+      sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.3"
+  hydrated_bloc:
+    dependency: "direct main"
+    description:
+      name: hydrated_bloc
+      sha256: c925e49704c052a8f249226ae7603f86bfa776b910816390763b956c71d2cbaf
+      url: "https://pub.dev"
+    source: hosted
+    version: "9.1.3"
+  intl:
+    dependency: transitive
+    description:
+      name: intl
+      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.18.1"
   material_color_utilities:
     dependency: transitive
     description:
@@ -63,10 +148,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+      sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
       url: "https://pub.dev"
     source: hosted
-    version: "1.9.1"
+    version: "1.10.0"
   nested:
     dependency: transitive
     description:
@@ -75,14 +160,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.0.0"
-  overlay_support:
-    dependency: "direct main"
-    description:
-      name: overlay_support
-      sha256: fc39389bfd94e6985e1e13b2a88a125fc4027608485d2d4e2847afe1b2bb339c
-      url: "https://pub.dev"
-    source: hosted
-    version: "2.1.0"
   path:
     dependency: transitive
     description:
@@ -91,6 +168,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.8.3"
+  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:
@@ -103,10 +204,10 @@ packages:
     dependency: transitive
     description:
       name: path_provider_platform_interface
-      sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
+      sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.1"
+    version: "2.1.2"
   path_provider_windows:
     dependency: transitive
     description:
@@ -119,34 +220,34 @@ packages:
     dependency: transitive
     description:
       name: platform
-      sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
+      sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.2"
+    version: "3.1.4"
   plugin_platform_interface:
     dependency: transitive
     description:
       name: plugin_platform_interface
-      sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
+      sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.6"
+    version: "2.1.8"
   provider:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: provider
-      sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
+      sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
       url: "https://pub.dev"
     source: hosted
-    version: "6.0.5"
+    version: "6.1.1"
   shared_preferences:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: shared_preferences
-      sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac
+      sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.2"
   shared_preferences_android:
     dependency: transitive
     description:
@@ -159,47 +260,71 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_foundation
-      sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
+      sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.4"
+    version: "2.3.5"
   shared_preferences_linux:
     dependency: transitive
     description:
       name: shared_preferences_linux
-      sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a
+      sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.1"
+    version: "2.3.2"
   shared_preferences_platform_interface:
     dependency: transitive
     description:
       name: shared_preferences_platform_interface
-      sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
+      sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.1"
+    version: "2.3.2"
   shared_preferences_web:
     dependency: transitive
     description:
       name: shared_preferences_web
-      sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
+      sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.2"
   shared_preferences_windows:
     dependency: transitive
     description:
       name: shared_preferences_windows
-      sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f
+      sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.1"
+    version: "2.3.2"
   sky_engine:
     dependency: transitive
     description: flutter
     source: sdk
     version: "0.0.99"
+  synchronized:
+    dependency: transitive
+    description:
+      name: synchronized
+      sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.0+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:
@@ -212,26 +337,26 @@ packages:
     dependency: transitive
     description:
       name: web
-      sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
+      sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
       url: "https://pub.dev"
     source: hosted
-    version: "0.1.4-beta"
+    version: "0.3.0"
   win32:
     dependency: transitive
     description:
       name: win32
-      sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
+      sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
       url: "https://pub.dev"
     source: hosted
-    version: "5.0.9"
+    version: "5.2.0"
   xdg_directories:
     dependency: transitive
     description:
       name: xdg_directories
-      sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
+      sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
       url: "https://pub.dev"
     source: hosted
-    version: "1.0.3"
+    version: "1.0.4"
 sdks:
-  dart: ">=3.1.0-185.0.dev <4.0.0"
-  flutter: ">=3.7.0"
+  dart: ">=3.2.0 <4.0.0"
+  flutter: ">=3.16.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 54385493e1b3a33abebd08a74d11c99fc6c6cdea..7f45afaa50d390a418c62fb833059908491b3c9a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,9 @@
-name: jeweled_game
+name: jeweled
 description: Jeweled Game
+
 publish_to: 'none'
-version: 1.0.0+1
+
+version: 0.0.9+9
 
 environment:
   sdk: '^3.0.0'
@@ -9,11 +11,27 @@ environment:
 dependencies:
   flutter:
     sdk: flutter
-  provider: ^6.0.5
-  shared_preferences: ^2.2.1
-  overlay_support: ^2.1.0
+  easy_localization: ^3.0.1
+  equatable: ^2.0.5
+  flutter_bloc: ^8.1.1
+  hydrated_bloc: ^9.0.0
+  path_provider: ^2.0.11
+  unicons: ^2.1.1
 
 flutter:
-  uses-material-design: true
+  uses-material-design: false
   assets:
     - assets/icons/
+    - 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