本記事はFlutterを使ったWordleクローンアプリ開発日記のpart5になります
以前の記事:
【アプリ開発日記】アプリを開発するにあたっての流れとコツ【part1】
【アプリ開発日記】Widgetを切り出して作成する【part2】
【アプリ開発日記】ブロックのWidgetを作成する【part3】
【アプリ開発日記】キーボードのイベントを通知する【part4】
これまでは、UIを中心に実装を進めてきました。Wordleクローンの部分に関してはUIが最低限実装できたので、次は文字を扱う基本機能の部分を実装します。
カスタムの例外を作成する
Widget側は文字の入力状態を何も気にしなくて良いように進めます。その際、Enterキーを押された際にエラーとなるパターンとして以下が考えられます。
- 判定した結果不正解だった
- 文字数エラー(5文字以外の状態)
- 単語が存在しないエラー(辞書に載っていない)
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回目の単語が入力できるようになっちゃってますね)。
コメントを残す