0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutterでビンゴを作成する

Last updated at Posted at 2021-09-25

概要

Flutterでビンゴを作成する

実行環境

Mac

$ sw_vers
ProductName:    macOS
ProductVersion: 11.6
BuildVersion:   20G165

Flutter

$ flutter --version
Flutter 2.5.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision ffb2ecea52 (5 days ago) • 2021-09-17 15:26:33 -0400
Engine • revision b3af521a05
Tools • Dart 2.14.2

仕様

  • ビンゴは5x5マス
  • アルファベットを重複なしでランダムにマスに配置する
  • リセットボタンを押すと最初に戻る
  • 抽選ボタンを押すとアルファベットが1文字抽選される
  • 縦横斜めいずれかに5マス連続で揃うとビンゴ
  • 抽選履歴が表示される

完成予定図

BINGO.png

手順

Flutterのプロジェクトを作成

flutter create --org xxx.xxx.xxx bingo

作成したプロジェクトに移動

cd bingo

アプリの内容

メイン処理

lib/main.dart
import 'dart:math';
import 'package:flutter/material.dart';

void main() => runApp(BingoApp());

class BingoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Bingo'),
          ),
          body: _BingoHome(),
        ),
      );
}

状態管理(StatefulWidget)

class _BingoHome extends StatefulWidget {
  @override
  State<_BingoHome> createState() => _BingoHomeState();
}

ステート

class _BingoHomeState extends State<_BingoHome> {
  List<Alphabet> _history = [];
  List<Alphabet> _remaining = [...Alphabet.values];
  List<List<Alphabet>> _cells = [];
  final _cellNum = 5;
  late Size _screenSize;
  late Orientation _orientation;
  static const _cellSizeMax = 80.0;
  late double _cellSize;
  late bool _bingo = false;

  @override
  void initState() {
    _cells = _CellGenerator(_cellNum).generate();
  }

  @override
  Widget build(BuildContext context) {
    _screenSize = MediaQuery.of(context).size;
    _orientation = MediaQuery.of(context).orientation;
    switch (_orientation) {
      case Orientation.landscape:
        _cellSize = [
          _screenSize.width / 10,
          _screenSize.height / 7,
          _cellSizeMax
        ].reduce(min);
        break;
      case Orientation.portrait:
        _cellSize = [
          _screenSize.width / 5,
          _screenSize.height / 10,
          _cellSizeMax
        ].reduce(min);
        break;
    }
    return _buildContainer();
  }

  Widget _buildContainer() => Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const SizedBox(height: _cellSizeMax / 2),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () => _pushReset(),
                child: const Text('Reset'),
              ),
              const SizedBox(width: _cellSizeMax / 2),
              ElevatedButton(
                onPressed: () => _pushLottery(),
                child: const Text('Lottery'),
              ),
            ],
          ),
          const SizedBox(height: _cellSizeMax / 2),
          _buildMainContainer(),
        ],
      );

  Widget _buildMainContainer() {
    switch (_orientation) {
      case Orientation.landscape:
        return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          _buildArea(),
          const SizedBox(width: _cellSizeMax / 2),
          _buildHistory(),
        ]);
      case Orientation.portrait:
        return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
          _buildArea(),
          const SizedBox(width: _cellSizeMax / 2),
          _buildHistory(),
        ]);
    }
  }

  Widget _buildArea() => SizedBox(
        width: _cellSize * _cellNum,
        height: _cellSize * _cellNum,
        child: Stack(
          children: [
            _buildColumn(),
            Center(
              child: Container(
                child: _buildInformationText(),
              ),
            ),
          ],
        ),
      );

  Widget? _buildInformationText() {
    if (_bingo) {
      return const Text(
        'BINGO!',
        style: TextStyle(
          fontWeight: FontWeight.bold,
          fontSize: 60.0,
          color: Colors.pink,
        ),
      );
    } else {
      return null;
    }
  }

  Widget _buildColumn() => Column(
        children: List.generate(
          _cellNum,
          (index) => _buildRow(index),
        ),
      );

  Widget _buildRow(int rowIndex) => Row(
        children:
            List.generate(_cellNum, (index) => _buildCell(rowIndex, index)),
      );

  Widget _buildCell(int rowIndex, int columnIndex) {
    final cell = _cells[rowIndex][columnIndex];

    return Container(
      width: _cellSize,
      height: _cellSize,
      decoration: buildBoxDecoration(rowIndex, columnIndex),
      child:
          Center(child: Text(cell.name, style: const TextStyle(fontSize: 18))),
    );
  }

  BoxDecoration buildBoxDecoration(int rowIndex, int columnIndex) {
    if (_history.contains(_cells[rowIndex][columnIndex])) {
      return BoxDecoration(
        border: Border.all(color: Colors.black),
        shape: BoxShape.rectangle,
        color: Colors.blue,
      );
    } else {
      return BoxDecoration(
        border: Border.all(color: Colors.black),
      );
    }
  }

  Widget _buildHistory() {
    switch (_orientation) {
      case Orientation.landscape:
        return Column(children: [
          const Center(child: Text('History', style: TextStyle(fontSize: 18))),
          Container(
            height: _cellSize * 4,
            width: _cellSize * 2,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black),
            ),
            child: GridView.count(
              crossAxisCount: 4,
              children: List.generate(_history.length,
                  (index) => Center(child: Text(_history[index].name))),
            ),
          )
        ]);
      case Orientation.portrait:
        return Column(children: [
          const Center(child: Text('History', style: TextStyle(fontSize: 18))),
          Container(
            height: _cellSize * 2,
            width: _cellSize * 5,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black),
            ),
            child: GridView.count(
              crossAxisCount: 10,
              children: List.generate(_history.length,
                  (index) => Center(child: Text(_history[index].name))),
            ),
          )
        ]);
    }
  }

  void _pushReset() {
    setState(() {
      _history = [];
      _remaining = [...Alphabet.values];
      _bingo = false;
      _cells = _CellGenerator(_cellNum).generate();
    });
  }

  void _pushLottery() {
    if (_bingo || _remaining.isEmpty) {
      return;
    }
    setState(() {
      _history.add(_remaining.removeAt(Random().nextInt(_remaining.length)));
      for (var rowIndex = 0; rowIndex < _cellNum; rowIndex++) {
        if (List.generate(_cellNum, (i) => i)
            .every((e) => _history.contains(_cells[rowIndex][e]))) {
          _bingo = true;
          break;
        }
      }
      for (var columnIndex = 0; columnIndex < _cellNum; columnIndex++) {
        if (List.generate(_cellNum, (i) => i)
            .every((e) => _history.contains(_cells[e][columnIndex]))) {
          _bingo = true;
          break;
        }
      }
      if (List.generate(_cellNum, (i) => i)
          .every((e) => _history.contains(_cells[e][e]))) {
        _bingo = true;
      }
      if (List.generate(_cellNum, (i) => i)
          .every((e) => _history.contains(_cells[e][_cellNum - e - 1]))) {
        _bingo = true;
      }
    });
  }
}

セルデータの作成

class _CellGenerator {
  final int _cellNum;
  final List<Alphabet> _remaining = [...Alphabet.values];

  _CellGenerator(this._cellNum);

  List<List<Alphabet>> generate() {
    var cells = List.generate(
        _cellNum,
        (_) => List.generate(_cellNum,
            (_) => _remaining.removeAt(Random().nextInt(_remaining.length))));
    return cells;
  }
}

セルデータ

enum Alphabet {
  A,
  B,
  C,
  D,
  E,
  F,
  G,
  H,
  I,
  J,
  K,
  L,
  M,
  N,
  O,
  P,
  Q,
  R,
  S,
  T,
  U,
  V,
  W,
  X,
  Y,
  Z
}

extension on Alphabet {
  String get name => toString().split('.').last;
}

アプリの実行

アプリを起動

flutter run

実行結果

iOS web
image.png image.png

GitHub

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?