Skip to content
Snippets Groups Projects
home.dart 12.14 KiB
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
        ),
      )
    );
  }
}