diff --git a/android/gradle.properties b/android/gradle.properties index 7e6b4cd36d9877b2acd1b762e65740d34ebaa0ce..d6af5de7dd3b01541eb540fa8dfd4d0caed97461 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.44 -app.versionCode=44 +app.versionName=0.0.45 +app.versionCode=45 diff --git a/fastlane/metadata/android/en-US/changelogs/45.txt b/fastlane/metadata/android/en-US/changelogs/45.txt new file mode 100644 index 0000000000000000000000000000000000000000..a4d7f20430102732867f5986ec0b7be51ab8ef36 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/45.txt @@ -0,0 +1 @@ +Improve / normalize display and layout diff --git a/fastlane/metadata/android/fr-FR/changelogs/45.txt b/fastlane/metadata/android/fr-FR/changelogs/45.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8d20383c47b581c6746ea27ec7eebb630a7554e --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/45.txt @@ -0,0 +1 @@ +Amélioration / standardisation de l'affichage diff --git a/lib/entities/moving_tile.dart b/lib/entities/moving_tile.dart index fc8d41a8f569d37a735381fc3559751af81df04f..0698ec2e30322ab98ff7763cfd0023cd8deb66bf 100644 --- a/lib/entities/moving_tile.dart +++ b/lib/entities/moving_tile.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; - -import 'tile.dart'; -import '../provider/data.dart'; +import 'package:puzzlegame/entities/tile.dart'; +import 'package:puzzlegame/provider/data.dart'; class MovingTile extends Tile { int currentCol; diff --git a/lib/entities/tile.dart b/lib/entities/tile.dart index 6e340c783f1e938e324292284a8568ed3fc62ff6..4971154a140e232ac46a329eb017d3b4c578a4a1 100644 --- a/lib/entities/tile.dart +++ b/lib/entities/tile.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../provider/data.dart'; +import 'package:puzzlegame/provider/data.dart'; class Tile { final Image image; diff --git a/lib/main.dart b/lib/main.dart index af052e9532a516544ad6c7b505202015c9f5d681..6a402c5316d364a5d88b81da8da40ff430b55d30 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; - -import 'provider/data.dart'; -import 'screens/home.dart'; +import 'package:puzzlegame/provider/data.dart'; +import 'package:puzzlegame/screens/home.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/provider/data.dart b/lib/provider/data.dart index f954001886496c618e2a8715eb08a9cfe44b753d..d685c364291b7429dd292f41b6e61275507eb8d7 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart'; - -import '../entities/moving_tile.dart'; +import 'package:puzzlegame/entities/moving_tile.dart'; class Data extends ChangeNotifier { // application configuration diff --git a/lib/screens/game.dart b/lib/screens/game.dart new file mode 100644 index 0000000000000000000000000000000000000000..09b9af1ef0f8ea46f53ded0c9cad26259c6c59d6 --- /dev/null +++ b/lib/screens/game.dart @@ -0,0 +1,220 @@ +import 'package:flutter/material.dart'; +import 'package:puzzlegame/entities/moving_tile.dart'; +import 'package:puzzlegame/provider/data.dart'; +import 'package:puzzlegame/utils/game_utils.dart'; + +class Game { + static void toggleDisplayTipImage(Data myProvider) { + myProvider.updateDisplayTipImage = !myProvider.displayTipImage; + } + + static Container buildTilesetWidget(Data myProvider) { + List<MovingTile> tiles = myProvider.tiles; + + Color borderColor = GameUtils.checkTilesetIsCleared(tiles) ? Colors.green : Colors.orange; + int tileIndex = 0; + + Table tileset = Table( + defaultColumnWidth: IntrinsicColumnWidth(), + border: TableBorder.all( + color: Colors.black, + style: BorderStyle.solid, + width: 2, + ), + children: [ + for (var row = 0; row < myProvider.tilesCount; row++) + TableRow( + children: [ + for (var col = 0; col < myProvider.tilesCount; col++) + Column(children: [tiles[tileIndex++].widget(myProvider)]), + ], + ), + ], + ); + + return Container( + margin: EdgeInsets.all(8), + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + color: borderColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: borderColor, + width: 8, + ), + ), + child: tileset, + ); + } + + static Container buildTipWidget(Data myProvider) { + return Container( + margin: EdgeInsets.all(2), + padding: EdgeInsets.all(2), + child: Table( + defaultColumnWidth: IntrinsicColumnWidth(), + children: [ + TableRow( + children: [ + Column( + children: [ + Image( + image: AssetImage('assets/images/placeholder.png'), + fit: BoxFit.fill, + ), + ], + ), + Column( + children: [ + TextButton( + child: Container( + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: Colors.blue, + width: 4, + ), + ), + child: Image( + image: AssetImage( + myProvider.displayTipImage + ? GameUtils.getImageAssetName(myProvider.image) + : 'assets/icons/tip_hidden.png', + ), + fit: BoxFit.contain, + ), + ), + onPressed: () => Game.toggleDisplayTipImage(myProvider), + ), + ], + ), + Column( + children: [ + Image( + image: AssetImage('assets/images/placeholder.png'), + fit: BoxFit.fill, + ), + ], + ), + ], + ), + ], + ), + ); + } + + static Container buildGameWidget(Data myProvider) { + return Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 8), + buildTopIndicatorWidget(myProvider), + SizedBox(height: 2), + Expanded( + child: buildGameBoard(myProvider), + ), + SizedBox(height: 2), + Container( + child: GameUtils.checkTilesetIsCleared(myProvider.tiles) + ? Game.buildWinMessage(myProvider) + : Game.buildTipWidget(myProvider), + ), + ], + ), + ); + } + + static Container buildGameBoard(Data myProvider) { + return Container( + margin: EdgeInsets.all(4), + padding: EdgeInsets.all(4), + child: Column( + children: [ + buildTilesetWidget(myProvider), + ], + ), + ); + } + + static Widget buildTopIndicatorWidget(Data myProvider) { + return Table( + children: [ + TableRow( + children: [ + Column(children: []), + Column(children: []), + ], + ), + ], + ); + } + + static Container buildWinMessage(Data myProvider) { + return Container( + margin: EdgeInsets.all(2), + padding: EdgeInsets.all(2), + child: Table( + defaultColumnWidth: IntrinsicColumnWidth(), + children: [ + TableRow( + children: [ + Column( + children: [ + Image( + image: AssetImage('assets/icons/game_win.png'), + fit: BoxFit.fill, + ), + ], + ), + Column( + children: [ + TextButton( + child: Container( + child: Image( + image: AssetImage('assets/icons/button_back.png'), + fit: BoxFit.fill, + ), + ), + onPressed: () => GameUtils.resetGame(myProvider), + ), + ], + ), + Column( + children: [ + Image( + image: AssetImage('assets/icons/game_win.png'), + fit: BoxFit.fill, + ), + ], + ), + ], + ), + ], + ), + ); + } + + static Container buildShufflingIndicatorWidget() { + return Container( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'â³', + style: TextStyle( + fontSize: 60, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + SizedBox(height: 20), + CircularProgressIndicator(), + ], + ), + ); + } +} diff --git a/lib/screens/home.dart b/lib/screens/home.dart index cf92623c8de8a51f9f121c3e9ffd738c02ff331c..9f5697a267fe23e66befdff69771fbac659a6c65 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,16 +1,12 @@ import 'dart:async'; -import 'dart:typed_data'; -import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show rootBundle; -import 'package:image/image.dart' as imglib; -import 'package:provider/provider.dart'; -import '../entities/tile.dart'; -import '../entities/moving_tile.dart'; -import '../provider/data.dart'; -import '../utils/get_images_list.dart'; +import 'package:provider/provider.dart'; +import 'package:puzzlegame/provider/data.dart'; +import 'package:puzzlegame/screens/game.dart'; +import 'package:puzzlegame/utils/game_utils.dart'; +import 'package:puzzlegame/utils/get_images_list.dart'; class Home extends StatelessWidget { static const String id = 'home'; @@ -18,38 +14,23 @@ class Home extends StatelessWidget { final Color themePrimaryColor = Colors.blue; final int _selectImageColumnsCount = 3; - Future<void> resetGame(Data myProvider) async { - myProvider.updateImage = ''; - myProvider.updateDisplayTipImage = false; - } - - void toggleDisplayTipImage(Data myProvider) { - myProvider.updateDisplayTipImage = !myProvider.displayTipImage; - } - - String getImageAssetName(String imageCode) { - return 'assets/images/' + imageCode + '.png'; - } - Future<void> getImagesList(Data myProvider) async { GetImagesList getImagesList; getImagesList = GetImagesList(); await getImagesList.init(); if (getImagesList.images.isNotEmpty) { myProvider.updateImages = getImagesList.images; - shuffleImages(myProvider); + shuffleAvailableImages(myProvider); } } Future<void> selectImage(Data myProvider, String imageCode, double tileImageSize) async { myProvider.updateIsShufflingBoard = true; myProvider.updateImage = imageCode; - new Timer(new Duration(seconds: 1), () { - splitImageInTiles(myProvider, tileImageSize); - }); + GameUtils.startGame(myProvider, tileImageSize); } - void shuffleImages(Data myProvider) { + void shuffleAvailableImages(Data myProvider) { List images = myProvider.images; images.shuffle(); myProvider.updateImages = images; @@ -70,7 +51,7 @@ class Home extends StatelessWidget { ), ), child: Image( - image: AssetImage(getImageAssetName(image)), + image: AssetImage(GameUtils.getImageAssetName(image)), fit: BoxFit.fill, ), ), @@ -146,257 +127,6 @@ class Home extends StatelessWidget { ); } - List<MovingTile> shuffleMovingTiles(List<MovingTile> tiles) { - var random = new Random(); - int tilesCount = tiles.length; - - for (int i = 0; i < (10 * tilesCount); i++) { - int indexTile1 = random.nextInt(tilesCount); - int indexTile2 = random.nextInt(tilesCount); - - MovingTile swap = tiles[indexTile1]; - tiles[indexTile1] = tiles[indexTile2]; - tiles[indexTile2] = swap; - - int swapCol = tiles[indexTile1].currentCol; - tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; - tiles[indexTile2].currentCol = swapCol; - - int swapRow = tiles[indexTile1].currentRow; - tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; - tiles[indexTile2].currentRow = swapRow; - } - - return tiles; - } - - Future<void> splitImageInTiles(Data myProvider, double tileImageSize) async { - String imageAsset = getImageAssetName(myProvider.image); - Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); - - imglib.Image image = imglib.decodeImage(imageData) ?? imglib.Image.fromBytes(1, 1, []); - - int x = 0, y = 0; - int width = (image.width / myProvider.tilesCount).round(); - int height = (image.height / myProvider.tilesCount).round(); - - List<MovingTile> tiles = []; - for (int i = 0; i < myProvider.tilesCount; i++) { - for (int j = 0; j < myProvider.tilesCount; j++) { - Uint8List tileData = new Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( - image, - x, - y, - width, - height, - ))); - - tiles.add(MovingTile( - Image.memory(tileData), - tileImageSize, - j, - i, - j, - i, - )); - - x += width; - } - x = 0; - y += height; - } - - myProvider.updateTiles = shuffleMovingTiles(tiles); - myProvider.updateIsShufflingBoard = false; - } - - bool _checkTilesetIsCleared(List<MovingTile> tiles) { - for (Tile tile in tiles) { - if (!tile.isCorrect()) { - return false; - } - } - return true; - } - - Container _buildTilesetWidget(Data myProvider) { - List<MovingTile> tiles = myProvider.tiles; - - Color borderColor = _checkTilesetIsCleared(tiles) ? Colors.green : Colors.orange; - int tileIndex = 0; - - Table tileset = Table( - defaultColumnWidth: IntrinsicColumnWidth(), - border: TableBorder.all( - color: Colors.black, - style: BorderStyle.solid, - width: 2, - ), - children: [ - for (var row = 0; row < myProvider.tilesCount; row++) - TableRow( - children: [ - for (var col = 0; col < myProvider.tilesCount; col++) - Column(children: [tiles[tileIndex++].widget(myProvider)]), - ], - ), - ], - ); - - return Container( - margin: EdgeInsets.all(8), - padding: EdgeInsets.all(8), - decoration: BoxDecoration( - color: borderColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: borderColor, - width: 8, - ), - ), - child: tileset, - ); - } - - Container _buildTipWidget(Data myProvider) { - return Container( - margin: EdgeInsets.all(2), - padding: EdgeInsets.all(2), - child: Table( - defaultColumnWidth: IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - Column( - children: [ - Image( - image: AssetImage('assets/images/placeholder.png'), - fit: BoxFit.fill, - ), - ], - ), - Column( - children: [ - TextButton( - child: Container( - // margin: EdgeInsets.all(4), - // padding: EdgeInsets.all(4), - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Colors.blue, - width: 4, - ), - ), - child: Image( - image: AssetImage( - myProvider.displayTipImage - ? getImageAssetName(myProvider.image) - : 'assets/icons/tip_hidden.png', - ), - fit: BoxFit.contain, - ), - ), - onPressed: () => toggleDisplayTipImage(myProvider), - ), - ], - ), - Column( - children: [ - Image( - image: AssetImage('assets/images/placeholder.png'), - fit: BoxFit.fill, - ), - ], - ), - ], - ), - ], - ), - ); - } - - Container _buildGameWidget(Data myProvider) { - return Container( - child: Column( - children: [ - _buildTilesetWidget(myProvider), - SizedBox(height: 20), - _checkTilesetIsCleared(myProvider.tiles) - ? _buildWinMessage(myProvider) - : _buildTipWidget(myProvider), - ], - ), - ); - } - - Container _buildWinMessage(Data myProvider) { - return Container( - margin: EdgeInsets.all(2), - padding: EdgeInsets.all(2), - child: Table( - defaultColumnWidth: IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - Column( - children: [ - Image( - image: AssetImage('assets/icons/game_win.png'), - fit: BoxFit.fill, - ), - ], - ), - Column( - children: [ - TextButton( - child: Container( - child: Image( - image: AssetImage('assets/icons/button_back.png'), - fit: BoxFit.fill, - ), - ), - onPressed: () => resetGame(myProvider), - ), - ], - ), - Column( - children: [ - Image( - image: AssetImage('assets/icons/game_win.png'), - fit: BoxFit.fill, - ), - ], - ), - ], - ), - ], - ), - ); - } - - Container _buildShufflingIndicatorWidget() { - return Container( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'â³', - style: TextStyle( - fontSize: 60, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - SizedBox(height: 20), - CircularProgressIndicator(), - ], - ), - ); - } - @override Widget build(BuildContext context) { Data _myProvider = Provider.of<Data>(context); @@ -406,13 +136,13 @@ class Home extends StatelessWidget { double _tileImageSize = (MediaQuery.of(context).size.width - 70) / _myProvider.tilesCount; if (_myProvider.isShufflingBoard) { - content = _buildShufflingIndicatorWidget(); + content = Game.buildShufflingIndicatorWidget(); precacheImage(new AssetImage('assets/icons/game_win.png'), context); } else { if (_myProvider.image == '') { content = _buildImageSelector(_myProvider, _tileImageSize); } else { - content = _buildGameWidget(_myProvider); + content = Game.buildGameWidget(_myProvider); } } @@ -434,7 +164,7 @@ class Home extends StatelessWidget { fit: BoxFit.fill, ), ), - onPressed: () => shuffleImages(_myProvider), + onPressed: () => shuffleAvailableImages(_myProvider), ), ]; @@ -453,7 +183,7 @@ class Home extends StatelessWidget { fit: BoxFit.fill, ), ), - onPressed: () => resetGame(_myProvider), + onPressed: () => GameUtils.resetGame(_myProvider), ), ]; diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart new file mode 100644 index 0000000000000000000000000000000000000000..ec6b9c63af8e2ee4779bf16b9d6ffaaec74ed343 --- /dev/null +++ b/lib/utils/game_utils.dart @@ -0,0 +1,100 @@ +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:image/image.dart' as imglib; +import 'package:puzzlegame/entities/moving_tile.dart'; +import 'package:puzzlegame/entities/tile.dart'; +import 'package:puzzlegame/provider/data.dart'; + +class GameUtils { + static String getImageAssetName(String imageCode) { + return 'assets/images/' + imageCode + '.png'; + } + + static startGame(Data myProvider, double tileImageSize) { + new Timer(new Duration(seconds: 1), () { + GameUtils.splitImageInTiles(myProvider, tileImageSize); + }); + } + + static Future<void> resetGame(Data myProvider) async { + myProvider.updateImage = ''; + myProvider.updateDisplayTipImage = false; + } + + static bool checkTilesetIsCleared(List<MovingTile> tiles) { + for (Tile tile in tiles) { + if (!tile.isCorrect()) { + return false; + } + } + return true; + } + + static List<MovingTile> shuffleMovingTiles(List<MovingTile> tiles) { + var random = new Random(); + int tilesCount = tiles.length; + + for (int i = 0; i < (10 * tilesCount); i++) { + int indexTile1 = random.nextInt(tilesCount); + int indexTile2 = random.nextInt(tilesCount); + + MovingTile swap = tiles[indexTile1]; + tiles[indexTile1] = tiles[indexTile2]; + tiles[indexTile2] = swap; + + int swapCol = tiles[indexTile1].currentCol; + tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; + tiles[indexTile2].currentCol = swapCol; + + int swapRow = tiles[indexTile1].currentRow; + tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; + tiles[indexTile2].currentRow = swapRow; + } + + return tiles; + } + + static Future<void> splitImageInTiles(Data myProvider, double tileImageSize) async { + String imageAsset = getImageAssetName(myProvider.image); + Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); + + imglib.Image image = imglib.decodeImage(imageData) ?? imglib.Image.fromBytes(1, 1, []); + + int x = 0, y = 0; + int width = (image.width / myProvider.tilesCount).round(); + int height = (image.height / myProvider.tilesCount).round(); + + List<MovingTile> tiles = []; + for (int i = 0; i < myProvider.tilesCount; i++) { + for (int j = 0; j < myProvider.tilesCount; j++) { + Uint8List tileData = new Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( + image, + x, + y, + width, + height, + ))); + + tiles.add(MovingTile( + Image.memory(tileData), + tileImageSize, + j, + i, + j, + i, + )); + + x += width; + } + x = 0; + y += height; + } + + myProvider.updateTiles = shuffleMovingTiles(tiles); + myProvider.updateIsShufflingBoard = false; + } +}