FlutterとSEO、ときどき仕事

Dart, flutter, アプリ開発日記

【アプリ開発日記】ブロックのWidgetを作成する【part3】

本記事はFlutterを使ったWordleクローンアプリ開発日記のpart3になります

以前の記事:

【アプリ開発日記】アプリを開発するにあたっての流れとコツ【part1】

【アプリ開発日記】Widgetを切り出して作成する【part2】

UIの文字入力ブロックを作成する

文字入力欄の1行分をBlocksクラスとして定義し、1文字分のブロックをCharBlockクラスとして定義します。

まずはCharBlockクラスには以下の情報を持たせます。

  • 1文字分の文字データ
  • 文字判定状態

文字判定状態には、全部で以下の4つがあるため、enumで定義します。cont intなどで定義しても良いですが、入力誤りや例外を無くすためにも列挙型を採用しています。

enum WordState {
  blank, // 未判定
  hit, // 文字も場所も合っている(緑)
  blow, // 文字が含まれているが場所が異なっている(黄)
  miss, // 文字が含まれていない(灰)
}

実際のコードは以下の感じです。キーボードの際と同じようにTextの周りに黒枠を表示することでWordleのUIに似せた形で表示できるようにしています。また、enumに応じた色を定義するために、getColor関数を用意しています。build関数の中で書いても良いですが、他の状態が増えたり、細かい色の調整のためのコードが増えたりすると可読性が下がるため、別関数に切り出しています。

/// 1ブロックを表すWidget
const double kBlockSize = 60.0; // 1ブロックのサイズを表す定数
class CharBlock extends StatelessWidget {
  final String char;
  final WordState state;

  const CharBlock({
    Key? key,
    required this.char,
    required this.state
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(1),
      decoration: BoxDecoration(
        color: getColor(),
        border: Border.all(color: Colors.black), // 四角の黒の枠線を追加
      ),
      width: kBlockSize,
      height: kBlockSize,
      alignment: Alignment.center,
      child: Text(
        char,
        style: const TextStyle(
          fontSize: 24,
          color: Colors.black,
          decoration: null,
        ),
      ),
    );
  }

  Color? getColor() {
    switch(state) {
      case WordState.hit:
        return Colors.green[500];
      case WordState.blow:
        return Colors.yellow[300];
      case WordState.miss:
        return Colors.grey[700];
      case WordState.blank:
      default:
        return Colors.grey[50];
    }
  }
}

次に1行分のデータを定義するBlocksクラスです。

こちらは特に言うことはありませんが、コンストラクタでwordStateListの初期値を設定しています(requierdにはしていません)。都度、すべて未判定のリストを渡して更新するのもプログラムが助長となりそうだったので、初期値をすべて未判定のリストとして設定しています。

class Blocks extends StatelessWidget {
  const Blocks({
    Key? key,
    required this.word,
    this.wordStateList = kDefaultStates,
  }) : super(key: key);

  static const int kWordLen = 5; // FIXME 推定する文字数(他クラスでも使うため他で定義しても良さそう)
  static const List<WordState> kDefaultStates = [WordState.blank, WordState.blank, WordState.blank, WordState.blank, WordState.blank]; // 状態の初期値

  final List<WordState> ?wordStateList; // 1行分の文字判定状態
  final String word; // 入力された文字

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: createBlockLine(word),
        ),
      ],
    );
  }

  /// 1行分のブロックを生成する
  List<Widget> createBlockLine(String word) {
    List<CharBlock> list = [];
    for (int i = 0; i < Blocks.kWordLen; i++) {
      String char = '';
      if (i < word.length) { // 5文字未満の文字数が入力された場合は空文字が渡るようにする
        char = word[i];
      }
      list.add(CharBlock(char: char, state: wordStateList![i]));
    }
    return list;
  }

 

1行だけ表示するとこんな感じ。

続きのpart4はこちら

【アプリ開発日記】キーボードのイベントを通知する【part4】

コメントを残す