👌
問題を解くことに集中できるシンプルな暗記アプリを作ってリリースした
こんにちは。今回は新しいiOS/Androidアプリ「Menma」をリリースしたので紹介します。
リリースしたアプリ
なぜ作ったか
以下の条件を満たす暗記アプリが欲しかったから
- 課金したくない
- 問題解けて、問題にチェック入れられれば良い
- ログインしたくない
- csvとかからインポートできるようにしたい
- 問題文にカンマ入ることあるので、結果的にはtsvを採用
- 余計な機能はいらない
- AIとかもいらない、忘却曲線考慮しなくて良い。どうせ何回も繰り返すので。
やらなかったこと
個人的には、何をやらないか、が、あらゆる物事で重要だと感じます。
- 問題の編集
これは面倒くさいので削りました。問題の編集はPCでやれば良いかなと思ってます。
ただ、テストを新たに作ると、テストの履歴とかも消えちゃうので、そこは、今後改良していきたいです。
- デザインと画面数
余白とテキストをちょっといじるだけで、あとはデフォルト。
画面数は絶対最小限にする。
使用技術/アーキテクチャ
プラットフォーム
Flutter
アーキテクチャ/状態管理
アーキテクチャ: MVVMっぽい
状態管理: hooks_riverpod
- Model
- View
- ViewModel
- riverpodのNotifierで作る
ただし、ViewModel作るほどではないなーと思ったら、useStateで済ませちゃってます。
グローバルで状態を持っておきたいなーと思ったら、Providerにしたりと臨機応変にやってます。
この辺は、Web(React)とネイティブアプリの良いとこ取りができるなと感じました。
ディレクトリ構造
機能ごとに切るか、レイヤーごとに切るか、で大きく2つに分かれると思いますが、私はfeatures
で機能ごとにディレクトリを切るのが好きです。
VSCodeの上の方に表示されるファイルのパスを見ただけで、今開いているファイルがどのメイン機能でどのサブ機能に関係しているのか、粒度も含めて直感的にわかるからです。
ハマった/勉強になった
他に特に書くこともないので、ちょっと手間取った点でも書いておきます
sqflite
久しぶりにちゃんとSQL書きました。DBの設計経験は積んでいきたいですね
リレーションで取ってくるときはちょっと面倒くさい。もっと良い書き方あるか?
factory TestQuestion.fromJson(Map<String, dynamic> json) {
return TestQuestion(
id: json['id'],
testId: json['test_id'],
question: Question.fromJson({
'id': json['question_id'],
'no': json['question_no'],
'question_set_id': json['question_question_set_id'],
'statement': json['question_statement'],
'option1': json['question_option1'],
'option2': json['question_option2'],
'option3': json['question_option3'],
'option4': json['question_option4'],
'answer': json['question_answer'],
'date_modified': json['question_date_modified'],
'is_checked': json['question_is_checked'],
}),
dateMyResponseStarted: json['date_my_response_started'],
dateMyResponseEnded: json['date_my_response_ended'],
isCorrectInt: json['is_correct'],
myResponse: json['my_response'],
);
List<Map<String, dynamic>> result = await _database!.rawQuery('''
SELECT test_questions.id as id,
test_questions.test_id as test_id,
test_questions.date_my_response_started as date_my_response_started,
test_questions.date_my_response_ended as date_my_response_ended,
test_questions.is_correct as is_correct,
test_questions.my_response as my_response,
questions.id as question_id,
questions.no as question_no,
questions.question_set_id as question_question_set_id,
questions.statement as question_statement,
questions.option1 as question_option1,
questions.option2 as question_option2,
questions.option3 as question_option3,
questions.option4 as question_option4,
questions.answer as question_answer,
questions.date_modified as question_date_modified,
questions.is_checked as question_is_checked
FROM test_questions
JOIN questions ON test_questions.question_id = questions.id
WHERE test_questions.id = ?
''', [id.toString()]);
あと、DBの配置場所も今回は考えてみました。
AndroidでlauchUrlするときにAndroidManifest.xmlに以下設定必要
pageCntroller.nextPageで複数ページ移動することがある
これっぽい感じだけど、なにか違うような??
とりあえずjumpToPage
で回避した
pageController.jumpToPage((nowIndex.value) + 1);
Flutter3.22のiOS Simulatorで表示がおかしくなる
早く直ってくれー
TODO
- Quizletのサブスクを解除する
Discussion