From 2f684d13f5e49311b4de9298185209d04692f002 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Wed, 30 Nov 2022 22:58:57 +0100
Subject: [PATCH] Fully draw board, without asset images

---
 android/gradle.properties                     |   4 +-
 assets/icons/skin_default.png                 | Bin 209 -> 0 bytes
 assets/skins/default_0.png                    | Bin 251 -> 0 bytes
 assets/skins/default_1.png                    | Bin 251 -> 0 bytes
 assets/skins/default_2.png                    | Bin 251 -> 0 bytes
 assets/skins/default_3.png                    | Bin 251 -> 0 bytes
 assets/skins/default_4.png                    | Bin 251 -> 0 bytes
 assets/skins/default_5.png                    | Bin 251 -> 0 bytes
 assets/skins/default_6.png                    | Bin 251 -> 0 bytes
 assets/skins/default_7.png                    | Bin 251 -> 0 bytes
 assets/skins/default_8.png                    | Bin 251 -> 0 bytes
 .../metadata/android/en-US/changelogs/7.txt   |   1 +
 .../metadata/android/fr-FR/changelogs/7.txt   |   1 +
 icons/build_game_icons.sh                     |  37 --------
 icons/skins/default/0.svg                     |   2 -
 icons/skins/default/1.svg                     |   2 -
 icons/skins/default/2.svg                     |   2 -
 icons/skins/default/3.svg                     |   2 -
 icons/skins/default/4.svg                     |   2 -
 icons/skins/default/5.svg                     |   2 -
 icons/skins/default/6.svg                     |   2 -
 icons/skins/default/7.svg                     |   2 -
 icons/skins/default/8.svg                     |   2 -
 lib/entities/cell.dart                        |  42 ---------
 lib/layout/board.dart                         |  48 +++++-----
 lib/layout/board_painter.dart                 |  88 ++++++++++++++++++
 lib/layout/color_theme.dart                   |  26 ++++++
 lib/layout/game.dart                          |   4 +-
 lib/provider/data.dart                        |   6 --
 lib/screens/home.dart                         |  45 +--------
 30 files changed, 146 insertions(+), 174 deletions(-)
 delete mode 100644 assets/icons/skin_default.png
 delete mode 100644 assets/skins/default_0.png
 delete mode 100644 assets/skins/default_1.png
 delete mode 100644 assets/skins/default_2.png
 delete mode 100644 assets/skins/default_3.png
 delete mode 100644 assets/skins/default_4.png
 delete mode 100644 assets/skins/default_5.png
 delete mode 100644 assets/skins/default_6.png
 delete mode 100644 assets/skins/default_7.png
 delete mode 100644 assets/skins/default_8.png
 create mode 100644 fastlane/metadata/android/en-US/changelogs/7.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/7.txt
 delete mode 100644 icons/skins/default/0.svg
 delete mode 100644 icons/skins/default/1.svg
 delete mode 100644 icons/skins/default/2.svg
 delete mode 100644 icons/skins/default/3.svg
 delete mode 100644 icons/skins/default/4.svg
 delete mode 100644 icons/skins/default/5.svg
 delete mode 100644 icons/skins/default/6.svg
 delete mode 100644 icons/skins/default/7.svg
 delete mode 100644 icons/skins/default/8.svg
 create mode 100644 lib/layout/board_painter.dart
 create mode 100644 lib/layout/color_theme.dart

diff --git a/android/gradle.properties b/android/gradle.properties
index 135006f..85b94f8 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.6
-app.versionCode=6
+app.versionName=0.0.7
+app.versionCode=7
diff --git a/assets/icons/skin_default.png b/assets/icons/skin_default.png
deleted file mode 100644
index fbe9cb151a27e54b848f7cd48e397cd308f51dad..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 209
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6Ow0@n4Emc|rZ6xta29w(7BetNp8{b<xs?;d
z85kHOOI#yLg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kz!zA;0f>vab;j&;9}#^
zku=_ybuWLT*gFOW1~*R^$B>N1x3>&=859Iq4zj0uIP<GH9xjzPy4~Nr_SJQZz}3%s
iZ!zEkADBDbXV=+Uv~<5Jn~-e-Qs(LE=d#Wzp$Py8rZ`0a

diff --git a/assets/skins/default_0.png b/assets/skins/default_0.png
deleted file mode 100644
index e543c1a9c0a2f38128c5a48437dcb75ceeaa23e2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;#yx{zi82-
z{rmSHK79E9|Nr}Q&+TMjV951!aSW-L^Y#uSCxe0j%fZ(PtlS;ENB+(9`S{>)PGjZ<
u&;Q5ta#Uw--vDw61DT-Vf5WG(zTf5Q&l~Qxw|m(IGRD)@&t;ucLK6VUxkt7D

diff --git a/assets/skins/default_1.png b/assets/skins/default_1.png
deleted file mode 100644
index 50ac7e9dc99ea6cf1e0a0126615e0217ec140023..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;#w>#KS@<%
zjkf*<9fN09_H}zyb}%q7<a)X|hE&XXdxw#eK|z4!;Ohid?hf7~|7QApd~i6YF>{0G
t|6_VNsx!B50J(&LOwjPZ;nP;%?{f9$4R_nyz3c)R<LT<>vd$@?2>>OWK{5aU

diff --git a/assets/skins/default_2.png b/assets/skins/default_2.png
deleted file mode 100644
index cca70ea9b7cb61319c79a07d8567ae9707f29a8a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;%ebm=N`E*
zBy(R__Q8UlzxP@M-ZC&S<a)X|hE&XXdxw#eK|z4!;Ohid?hf7~|7QApd~i6YF>{0G
t|6_VNsx!B50J(&LOwjPZ;nP;%?{f9$4R_nyz3c)R<LT<>vd$@?2>^wIL$3e;

diff --git a/assets/skins/default_3.png b/assets/skins/default_3.png
deleted file mode 100644
index 9e4ccd82edd6dea24e3f73a9794d735a268cc11b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;wt4QrIf6s
zQL3R?u4y{QRN+*5D+2>VuBVG*NX4ADcNjSt6a-ifzD{7}?%+N0Z>G=32ZwVSGdFnt
sKc<(XI&=F5kV_cI1P%WiK5g~=E?0lvaJRkP%Px>Hp00i_>zopr086(&umAu6

diff --git a/assets/skins/default_4.png b/assets/skins/default_4.png
deleted file mode 100644
index 86a9c87e0459697060a91571714765c0078a1403..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;#!|Bwy0TV
z|18DB^OXLd(>a?hpTNMtkn8E<7*a9k?Hxu=1_c3@gRc`<xjT4|{F~|X@xkGo#>@?#
t|BvbAsLtHJ0pt<}GC{-thEH34zsuF1H{5M+_p%FQjHj!g%Q~loCIB)$Lk9o=

diff --git a/assets/skins/default_5.png b/assets/skins/default_5.png
deleted file mode 100644
index 7c4f1cb57586355b4e443d8e6e4d17fc7053f2d1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;#zO6xX4|5
zf3VTvP~-pk&MLF5)fpHVay?xfLn`LHy~D`Kpdi3<@O1(!cL(p0e=~hPJ~*7yn7P68
t|1rHB)tTEjfLy{rCTRHI@M){>ce(oWhP&<UUUq?u@pScbS?83{1OQD#L1_R0

diff --git a/assets/skins/default_6.png b/assets/skins/default_6.png
deleted file mode 100644
index 4a620e27de46cc9db3742e7d60a5d2e4c8094a4e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;u;_qk)~Eq
zsoPqu*S^qZ<DU!p3m6y}ay?xfLn`LHy~D`Kpdi3<@O1(!cL(p0e=~hPJ~*7yn7P68
t|1rHB)tTEjfLy{rCTRHI@M){>ce(oWhP&<UUUq?u@pScbS?83{1OV*PLZbix

diff --git a/assets/skins/default_7.png b/assets/skins/default_7.png
deleted file mode 100644
index 988d6fc485453b4f4c0c5c306809eec93a611718..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;wn>Lud-;7
z*8ct4hYwr)|KF=<TEW1;kn8E<7*a9k?Hxu=1_c3@gRc`<xjT4|{F~|X@xkGo#>@?#
t|BvbAsLtHJ0pt<}GC{-thEH34zsuF1H{5M+_p%FQjHj!g%Q~loCIENvLx}(Y

diff --git a/assets/skins/default_8.png b/assets/skins/default_8.png
deleted file mode 100644
index 1f5cc615ca867c90f0004097308dc83d9ae496df..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 251
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE6EX)iH3=2LLnlLaha29w(7Beu&M1U~k*%{Bz
zFfcGkmbgZg1m~xflqVLYGL)B>>t*I;7bhncr0V4trO$q6BgMeLz#rff;#z6xH_J0+
zYgpONh>B0ebIo4{cQ7z8<a)X|hE&XXdxw#eK|z4!;Ohid?hf7~|7QApd~i6YF>{0G
t|6_VNsx!B50J(&LOwjPZ;nP;%?{f9$4R_nyz3c)R<LT<>vd$@?2>@k!Lu&v4

diff --git a/fastlane/metadata/android/en-US/changelogs/7.txt b/fastlane/metadata/android/en-US/changelogs/7.txt
new file mode 100644
index 0000000..69bf984
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/7.txt
@@ -0,0 +1 @@
+Fully draw board without asset images, improve cells borders
diff --git a/fastlane/metadata/android/fr-FR/changelogs/7.txt b/fastlane/metadata/android/fr-FR/changelogs/7.txt
new file mode 100644
index 0000000..548a098
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/7.txt
@@ -0,0 +1 @@
+Dessine le jeu au lieu d'utiliser des images, amélioration des bordures de cellules
diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh
index b76b628..08f5571 100755
--- a/icons/build_game_icons.sh
+++ b/icons/build_game_icons.sh
@@ -32,24 +32,6 @@ AVAILABLES_GAME_SETTINGS="
   colors:5,6,7,8
 "
 
-# Skins
-AVAILABLE_SKINS="
-  default
-"
-
-# Images per skin
-SKIN_IMAGES="
-  0
-  1
-  2
-  3
-  4
-  5
-  6
-  7
-  8
-"
-
 #######################################################
 
 # optimize svg
@@ -105,19 +87,6 @@ function build_settings_icons() {
   done
 }
 
-function build_icon_for_skin() {
-  SKIN_CODE="$1"
-
-  # skin main image
-  build_icon ${CURRENT_DIR}/skin_${SKIN_CODE}.svg ${ASSETS_DIR}/icons/skin_${SKIN_CODE}.png
-
-  # skin images
-  for SKIN_IMAGE in ${SKIN_IMAGES}
-  do
-    build_icon ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png
-  done
-}
-
 #######################################################
 
 # Create output folders
@@ -139,9 +108,3 @@ for GAME_SETTING in ${AVAILABLES_GAME_SETTINGS}
 do
   build_settings_icons "${GAME_SETTING}"
 done
-
-# build skins images
-for SKIN in ${AVAILABLE_SKINS}
-do
-  build_icon_for_skin "${SKIN}"
-done
diff --git a/icons/skins/default/0.svg b/icons/skins/default/0.svg
deleted file mode 100644
index c55c649..0000000
--- a/icons/skins/default/0.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/1.svg b/icons/skins/default/1.svg
deleted file mode 100644
index 8381dc6..0000000
--- a/icons/skins/default/1.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#E63A3F" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/2.svg b/icons/skins/default/2.svg
deleted file mode 100644
index 8306614..0000000
--- a/icons/skins/default/2.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#708CFD" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/3.svg b/icons/skins/default/3.svg
deleted file mode 100644
index 5b6298b..0000000
--- a/icons/skins/default/3.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#359C35" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/4.svg b/icons/skins/default/4.svg
deleted file mode 100644
index b48f425..0000000
--- a/icons/skins/default/4.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#FFCE2C" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/5.svg b/icons/skins/default/5.svg
deleted file mode 100644
index 68b7610..0000000
--- a/icons/skins/default/5.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#FF6F43" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/6.svg b/icons/skins/default/6.svg
deleted file mode 100644
index da1c8ae..0000000
--- a/icons/skins/default/6.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#A13CB1" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/7.svg b/icons/skins/default/7.svg
deleted file mode 100644
index da0f69c..0000000
--- a/icons/skins/default/7.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#38FFFF" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/icons/skins/default/8.svg b/icons/skins/default/8.svg
deleted file mode 100644
index 0b637b3..0000000
--- a/icons/skins/default/8.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#F2739D" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".5" stroke-width="2.6"/></svg>
diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart
index d5f45d9..669d9fe 100644
--- a/lib/entities/cell.dart
+++ b/lib/entities/cell.dart
@@ -1,49 +1,7 @@
-import 'package:flutter/material.dart';
-import 'package:jeweled_game/provider/data.dart';
-import 'package:jeweled_game/utils/board_utils.dart';
-
 class Cell {
   String value = '0';
 
   Cell(
     this.value,
   );
-
-  /*
-  * Build widget for board cell, with interactions
-  * @TODO: remove parameters
-  */
-  Container widget(Data myProvider, int row, int col) {
-    String imageAsset = this.getImageAssetName(myProvider);
-
-    return Container(
-      child: GestureDetector(
-        child: AnimatedSwitcher(
-          duration: const Duration(milliseconds: 100),
-          transitionBuilder: (Widget child, Animation<double> animation) {
-            return ScaleTransition(child: child, scale: animation);
-          },
-          child: Image(
-            image: AssetImage(imageAsset),
-            fit: BoxFit.fill,
-            key: ValueKey<int>(imageAsset.hashCode),
-          ),
-        ),
-        onTap: () {
-          if (!myProvider.isGameFinished) {
-            BoardUtils.tapOnCell(myProvider, row, col);
-          }
-        },
-      ),
-    );
-  }
-
-  /*
-  * Compute image asset name, from skin and cell value/state
-  */
-  String getImageAssetName(Data myProvider) {
-    String imageAsset = 'assets/skins/' + myProvider.parameterSkin + '_' + this.value + '.png';
-
-    return imageAsset;
-  }
 }
diff --git a/lib/layout/board.dart b/lib/layout/board.dart
index 8d207d1..4cf8f3c 100644
--- a/lib/layout/board.dart
+++ b/lib/layout/board.dart
@@ -1,38 +1,38 @@
 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) {
+  static Container buildGameBoard(Data myProvider, double boardWidth) {
     return Container(
       margin: EdgeInsets.all(4),
       padding: EdgeInsets.all(4),
       child: Column(
         children: [
-          buildGameTileset(myProvider),
+          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,
+                  ),
+                ),
+              ),
+            ),
+          )
         ],
       ),
     );
   }
-
-  static Table buildGameTileset(Data myProvider) {
-    int boardSizeHorizontal = myProvider.sizeHorizontal;
-    int boardSizeVertical = myProvider.sizeVertical;
-
-    return Table(
-      defaultColumnWidth: IntrinsicColumnWidth(),
-      children: [
-        for (var row = 0; row < boardSizeVertical; row++)
-          TableRow(
-            children: [
-              for (var col = 0; col < boardSizeHorizontal; col++)
-                Column(
-                  children: [
-                    myProvider.getCell(row, col).widget(myProvider, row, col),
-                  ],
-                ),
-            ],
-          ),
-      ],
-    );
-  }
 }
diff --git a/lib/layout/board_painter.dart b/lib/layout/board_painter.dart
new file mode 100644
index 0000000..a758d37
--- /dev/null
+++ b/lib/layout/board_painter.dart
@@ -0,0 +1,88 @@
+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
new file mode 100644
index 0000000..ebc2309
--- /dev/null
+++ b/lib/layout/color_theme.dart
@@ -0,0 +1,26 @@
+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
index 12df4e3..4360e6a 100644
--- a/lib/layout/game.dart
+++ b/lib/layout/game.dart
@@ -4,7 +4,7 @@ import 'package:jeweled_game/provider/data.dart';
 import 'package:jeweled_game/utils/game_utils.dart';
 
 class Game {
-  static Container buildGameWidget(Data myProvider) {
+  static Container buildGameWidget(Data myProvider, double boardWidth) {
     bool gameIsFinished = myProvider.isGameFinished;
 
     return Container(
@@ -16,7 +16,7 @@ class Game {
           Game.buildTopIndicatorWidget(myProvider),
           SizedBox(height: 2),
           Expanded(
-            child: Board.buildGameBoard(myProvider),
+            child: Board.buildGameBoard(myProvider, boardWidth),
           ),
           SizedBox(height: 2),
           Container(
diff --git a/lib/provider/data.dart b/lib/provider/data.dart
index bbeabe5..c075159 100644
--- a/lib/provider/data.dart
+++ b/lib/provider/data.dart
@@ -34,7 +34,6 @@ class Data extends ChangeNotifier {
   String get parameterSkin => _parameterSkin;
 
   // Game data
-  bool _assetsPreloaded = false;
   bool _gameIsRunning = false;
   bool _gameIsFinished = false;
   int _sizeVertical = 0;
@@ -261,11 +260,6 @@ class Data extends ChangeNotifier {
     return {};
   }
 
-  bool get assetsPreloaded => _assetsPreloaded;
-  void updateAssetsPreloaded(bool assetsPreloaded) {
-    _assetsPreloaded = assetsPreloaded;
-  }
-
   List<List<Cell>> get cells => _cells;
   void updateCells(List<List<Cell>> cells) {
     _cells = cells;
diff --git a/lib/screens/home.dart b/lib/screens/home.dart
index ca3fb6d..6db733e 100644
--- a/lib/screens/home.dart
+++ b/lib/screens/home.dart
@@ -23,51 +23,10 @@ class _HomeState extends State<Home> {
     myProvider.loadCurrentSavedState();
   }
 
-  List getImagesAssets(Data myProvider) {
-    List assets = [];
-
-    List gameImages = [
-      'button_back',
-      'button_delete_saved_game',
-      'button_resume_game',
-      'button_start',
-      'game_fail',
-      'game_win',
-      'placeholder',
-    ];
-    myProvider.availableLevelValues.forEach((level) => gameImages.add('level_' + level));
-    myProvider.availableSizeValues.forEach((size) => gameImages.add('size_' + size));
-    myProvider.availableSkinValues.forEach((skin) => gameImages.add('skin_' + skin));
-
-    gameImages.forEach((image) => assets.add('assets/icons/' + image + '.png'));
-
-    List skinImages = [
-      '0',
-      '1',
-      '2',
-      '3',
-      '4',
-      '5',
-      '6',
-      '7',
-      '8',
-    ];
-
-    myProvider.availableSkinValues.forEach((skin) => skinImages
-        .forEach((image) => assets.add('assets/skins/' + skin + '_' + image + '.png')));
-
-    return assets;
-  }
-
   @override
   Widget build(BuildContext context) {
     Data myProvider = Provider.of<Data>(context);
-
-    if (!myProvider.assetsPreloaded) {
-      List assets = getImagesAssets(myProvider);
-      assets.forEach((asset) => precacheImage(AssetImage(asset), context));
-      myProvider.updateAssetsPreloaded(true);
-    }
+    double boardWidth = MediaQuery.of(context).size.width;
 
     List<Widget> menuActions = [];
 
@@ -100,7 +59,7 @@ class _HomeState extends State<Home> {
       body: SafeArea(
         child: Center(
           child: myProvider.isGameRunning
-              ? Game.buildGameWidget(myProvider)
+              ? Game.buildGameWidget(myProvider, boardWidth)
               : Parameters.buildParametersSelector(myProvider),
         ),
       ),
-- 
GitLab