👌

問題を解くことに集中できるシンプルな暗記アプリを作ってリリースした

2024/06/09に公開

こんにちは。今回は新しいiOS/Androidアプリ「Menma」をリリースしたので紹介します。

リリースしたアプリ

https://apps.apple.com/jp/app/menma/id6502872221

https://play.google.com/store/apps/details?id=com.dl10yr.menma2&hl=ja

なぜ作ったか

以下の条件を満たす暗記アプリが欲しかったから

  • 課金したくない
  • 問題解けて、問題にチェック入れられれば良い
  • ログインしたくない
  • csvとかからインポートできるようにしたい
    • 問題文にカンマ入ることあるので、結果的にはtsvを採用
  • 余計な機能はいらない
  • AIとかもいらない、忘却曲線考慮しなくて良い。どうせ何回も繰り返すので。

やらなかったこと

個人的には、何をやらないか、が、あらゆる物事で重要だと感じます。

  • 問題の編集

これは面倒くさいので削りました。問題の編集はPCでやれば良いかなと思ってます。
ただ、テストを新たに作ると、テストの履歴とかも消えちゃうので、そこは、今後改良していきたいです。

  • デザインと画面数

余白とテキストをちょっといじるだけで、あとはデフォルト。
画面数は絶対最小限にする。

使用技術/アーキテクチャ

プラットフォーム

Flutter

アーキテクチャ/状態管理

アーキテクチャ: MVVMっぽい
状態管理: hooks_riverpod

  • Model
  • View
  • ViewModel
    • riverpodのNotifierで作る

ただし、ViewModel作るほどではないなーと思ったら、useStateで済ませちゃってます。
グローバルで状態を持っておきたいなーと思ったら、Providerにしたりと臨機応変にやってます。

この辺は、Web(React)とネイティブアプリの良いとこ取りができるなと感じました。

ディレクトリ構造

機能ごとに切るか、レイヤーごとに切るか、で大きく2つに分かれると思いますが、私はfeaturesで機能ごとにディレクトリを切るのが好きです。

https://codewithandrea.com/articles/flutter-project-structure/

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の配置場所も今回は考えてみました。
https://zenn.dev/beeeyan/articles/b9f1b42de9cb67

AndroidでlauchUrlするときにAndroidManifest.xmlに以下設定必要

https://zenn.dev/joo_hashi/articles/ee377ae7f1fe05

pageCntroller.nextPageで複数ページ移動することがある

これっぽい感じだけど、なにか違うような??
https://github.com/flutter/flutter/issues/40026

とりあえずjumpToPageで回避した

pageController.jumpToPage((nowIndex.value) + 1);

Flutter3.22のiOS Simulatorで表示がおかしくなる

早く直ってくれー

https://github.com/flutter/flutter/issues/148660

TODO

  • Quizletのサブスクを解除する

Discussion