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 '../provider/data.dart'; import '../utils/get_images_list.dart'; class Home extends StatelessWidget { static const String id = 'home'; Color themePrimaryColor = Colors.blue; double _tipImageSize = 100; int _selectImageColumnsCount = 2; 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 != null) { myProvider.updateImages = getImagesList.images; shuffleImages(myProvider); } } Future<void> selectImage(Data myProvider, String imageCode, double tileImageSize) async { myProvider.updateIsShufflingBoard = true; myProvider.updateImage = imageCode; Timer timer = new Timer(new Duration(seconds: 1), () { splitImageInTiles(myProvider, tileImageSize); }); } void shuffleImages(Data myProvider) { List images = myProvider.images; images.shuffle(); myProvider.updateImages = images; } Container _buildImageSelectorItem(Data myProvider, String image, double selectImageSize, double tileImageSize) { return Container( child: FlatButton( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: Colors.blue[200], width: 4, ), ), margin: EdgeInsets.all(2), child: Image( image: AssetImage(getImageAssetName(image)), width: selectImageSize, height: selectImageSize, fit: BoxFit.fill ), ), onPressed: () { selectImage(myProvider, image, tileImageSize); }, ), ); } Container _buildImageSelector(Data myProvider, double size, double tileImageSize) { if (myProvider.images.length == 0) { getImagesList(myProvider); } List images = myProvider.images; return Container( padding: EdgeInsets.all(2), child: ListView( children: [ Table( defaultColumnWidth: IntrinsicColumnWidth(), children: [ for (var imageIndex = 0; imageIndex < images.length; imageIndex += _selectImageColumnsCount) TableRow( children: [ for (var columnIndex = 0; columnIndex < _selectImageColumnsCount; columnIndex++) Column( children: [ if (imageIndex + columnIndex < images.length) _buildImageSelectorItem(myProvider, images[imageIndex + columnIndex], size, tileImageSize) ] ), ] ), ], ), ], ), ); } FlatButton _buildTilesetSizeSelectorItem(Data myProvider, int value) { String assetName = 'assets/icons/difficulty_' + value.toString() + 'x' + value.toString() + '.png'; Color borderColor = themePrimaryColor; if (myProvider.tilesCount == value) { borderColor = Colors.white; } return FlatButton( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: borderColor, width: 4, ), ), margin: EdgeInsets.all(8), child: Image( image: AssetImage(assetName), fit: BoxFit.fill ), ), onPressed: () { myProvider.updateTilesCount = value; }, ); } List<Tile> shuffleTiles(List<Tile> 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); Tile 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); int x = 0, y = 0; int width = (image.width / myProvider.tilesCount).round(); int height = (image.height / myProvider.tilesCount).round(); List<Tile> tiles = List<Tile>(); for (int i = 0; i < myProvider.tilesCount; i++) { for (int j = 0; j < myProvider.tilesCount; j++) { tiles.add( Tile( Image.memory( imglib.encodeJpg( imglib.copyCrop(image, x, y, width, height) ) ), tileImageSize, j, i, j, i, ) ); x += width; } x = 0; y += height; } myProvider.updateTiles = shuffleTiles(tiles); myProvider.updateIsShufflingBoard = false; } bool _checkTilesetIsOrdered(List<Tile> tiles) { for (Tile tile in tiles) { if ( (tile.currentRow != tile.originalRow) || (tile.currentCol != tile.originalCol) ) { return false; } } return true; } Container _buildTilesetWidget(Data myProvider) { List tiles = myProvider.tiles; Color borderColor = _checkTilesetIsOrdered(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( child: FlatButton( 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/ui/tip-hidden.png' ), width: _tipImageSize, height: _tipImageSize, fit: BoxFit.fill ), ), onPressed: () => toggleDisplayTipImage(myProvider), ), ); } Container _buildGameWidget(Data myProvider) { return Container( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ _buildTilesetWidget(myProvider), SizedBox(height: 20), _checkTilesetIsOrdered(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: [ FlatButton( 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); var content; double _selectorImageSize = (MediaQuery.of(context).size.width - 140) / _selectImageColumnsCount; double _tileImageSize = (MediaQuery.of(context).size.width - 70) / _myProvider.tilesCount; if (_myProvider.isShufflingBoard) { content = _buildShufflingIndicatorWidget(); } else { if (_myProvider.image == '') { content = _buildImageSelector(_myProvider, _selectorImageSize, _tileImageSize); } else { content = _buildGameWidget(_myProvider); } } List<Widget> menuActions = [ _buildTilesetSizeSelectorItem(_myProvider, 3), _buildTilesetSizeSelectorItem(_myProvider, 4), _buildTilesetSizeSelectorItem(_myProvider, 5), FlatButton( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: themePrimaryColor, width: 4, ), ), margin: EdgeInsets.all(8), child: Image( image: AssetImage('assets/icons/button_shuffle.png'), fit: BoxFit.fill ), ), onPressed: () => shuffleImages(_myProvider), ), ]; List<Widget> gameActions = [ FlatButton( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: Colors.blue, width: 4, ), ), margin: EdgeInsets.all(8), child: Image( image: AssetImage('assets/icons/button_back.png'), fit: BoxFit.fill ), ), onPressed: () => resetGame(_myProvider), ), ]; return Scaffold( appBar: AppBar( backgroundColor: themePrimaryColor, actions: _myProvider.image == '' ? menuActions : gameActions, ), body: SafeArea( child: Center( child: content ), ) ); } }