FlutterとSEO、ときどき仕事

Dart, flutter, アプリ開発日記

【アプリ開発日記】Wordleの基本機能を実装する【part5】

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

以前の記事:

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

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

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

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

これまでは、UIを中心に実装を進めてきました。Wordleクローンの部分に関してはUIが最低限実装できたので、次は文字を扱う基本機能の部分を実装します。

カスタムの例外を作成する

Widget側は文字の入力状態を何も気にしなくて良いように進めます。その際、Enterキーを押された際にエラーとなるパターンとして以下が考えられます。

  1. 判定した結果不正解だった
  2. 文字数エラー(5文字以外の状態)
  3. 単語が存在しないエラー(辞書に載っていない)

Enterキーを押された際のAPIを作成する際、戻り値をbool型とすることで1番を扱うことが可能です(正解の場合はtrueを返し、不正解の場合はfalseを返す)。ただ、2番3番に関してはユーザにフィードバックが別途必要となるため、bool型の引数だけでは処理することができません。もちろん、戻り値をint型にし、それぞれのエラーであることがわかるようにしても良いですが、例外として扱うことで「判定が正しく行えていない」という意味を持たせています。

/// 文字の長さ判定エラー用Exception
class WordLengthException implements Exception {

}
/// 単語が存在しない場合用のException
class WordNotExistException implements Exception {

}

文字管理を行う

前述したとおり、Widget側では文字の入力状態を一切管理しなくて良いようにします。キーが入力された時用、DELキーが押された時用、Enterキーが押された時用の3つを用意します。長さチェックや単語が存在するかどうかのAPIはprivateとすることでWidget側からは見えないようにしています。

class WordController {
  static const kWordCount = 6; //チャレンジ回数
  static const kWordLength = 5; //単語の長さ

  int _currentCount = 0;
  final List<String> _wordList = ['', '', '', '', '', '']; // finalにしているが、間接的にデータの中身は書き換わる
  String answer = 'EARLY'; // FIXME
  WordController();

  void initAnswer() {
    // TODO 回答をランダムに設定する
  }

  /// 1文字を追加する
  void appendLetter(String letter) {
    if (_checkWordLength()) {
      //5文字になっている場合は追加しない
      return;
    }
    if (letter.length == 1) {
      _wordList[_currentCount] += letter;
    } else {
      // FIXME throw exception ?
    }
  }

  /// 末尾の文字を削除する
  void removeLetter() {
    if (_wordList[_currentCount].isNotEmpty) {
      _wordList[_currentCount] = _wordList[_currentCount].substring(
          0, _wordList[_currentCount].length - 1);
    }
  }

  bool enterLetter() {
    // 文字の長さを確認する
    if (!_checkWordLength()) {
      throw WordLengthException();
    }
    // 単語が存在するかどうか確認する
    if (!_isWordExist()) {
      throw WordNotExistException();
    }
    // 文字に問題がなければ答えと判定を行う
    if (answer == _wordList[_currentCount]) {
      return true;
    } else {
      // 誤りの場合はカウンターをインクリメントする
      _currentCount++;
      return false;
    }
  }

// 5文字かどうか判定する
  bool _checkWordLength() {
    return _wordList[_currentCount].length == 5;
  }

  bool _isWordExist() {
    // TODO 存在する英単語かどうか確認する
    return true;
  }
}

最後にmain.dart内のキーボードからのイベントを紐づけることで入力管理ができました。

SoftwareKeyBoard(
  onTapEnterKey: () {
    debugPrint('onTapEnterKey');
    try {
      if (controller.enterLetter()) {
        // 正解
        debugPrint('正解');
        const snackBar = SnackBar(content: Text('正解です'));
        ScaffoldMessenger.of(context).showSnackBar(snackBar);
      } else {
        // 不正解
        debugPrint('不正解');
        // TODO Widgetの更新
      }
    } on WordNotExistException catch(e) {
      debugPrint('WordNotExistException');
      const snackBar = SnackBar(content: Text('存在しない単語です'));
      ScaffoldMessenger.of(context).showSnackBar(snackBar);
    } on WordLengthException catch(e) {
      debugPrint('WordLengthException');
      const snackBar = SnackBar(content: Text('5文字入力してください'));
      ScaffoldMessenger.of(context).showSnackBar(snackBar);
    } catch(e) {
      // do nothing
      debugPrint('catch');
    }
  },
  onTapKey: (String char) {
    debugPrint('onTap : $char');
    controller.appendLetter(char);
  },
  onTapDeleteKey: () {
    debugPrint('onTapDeleteKey');
    controller.removeLetter();
  },
),

あとはUI更新、ゲーム開始・終了の判定ぐらいでしょうか(7回目の単語が入力できるようになっちゃってますね)。

次の記事:【アプリ開発日記】Wordleの基本機能を実装する【part6】

コメントを残す