はじめに
毎日決まった時間に飲む薬を、週間カレンダーで記録するアプリ「忘れなウィーク」を Flutter と Cursor で作りました。データは端末内のみ、アカウント不要です。この記事では技術スタック、苦労した点、こだわった箇所をまとめます。
技術スタック
- Flutter (Dart 3, Material 3) … メインは Android。Web は Drift の wasm 配置で対応可能。
- Drift … SQLite の型安全ラッパー。テーブルは medicines(薬マスタ)・medicine_schedules(時間帯)・medicine_logs(服用記録)の 3 つ。マイグレーションで sortOrder 追加などに対応。
- リマインダー … flutter_local_notifications + timezone。Android のみ。時間帯ごとに 1 時刻と ON/OFF を SharedPreferences で永続化し、薬が 1 つでもある時間帯だけ日次で通知。
- アーキテクチャ … DatabaseScope(InheritedWidget)で DB を注入。下タブは IndexedStack + NavigationBar で、表示中タブだけ再読み込みする設計。
苦労した点
週間カレンダーで「○」がつかない
記録を保存しても週間画面に戻ると ○ にならないことがありました。原因は、薬を削除したあともメモリ上の「その時間帯の薬一覧」に削除済み ID が残り、「全員飲んだ」判定がずれていたことです。対処として、スケジュール取得時に medicines に存在する薬だけ を対象にし、記録保存後はスケジュールと週ログを まとめて 1 回で取得・setState する _refreshSchedulesAndWeekLogs を呼ぶようにしました。
リマインダーを確実に届ける
exactAllowWhileIdle だと端末によっては通知が届かないことがあるため、inexactAllowWhileIdle にし、設定時刻の前後で届く形にしました。タイムゾーンは Asia/Tokyo 固定で、依存を増やさずに済ませています。Android 8 以降は通知チャンネルを初期化時に必ず作成しています。
その他
- リマインダーは Android のみなので、Platform.isAndroid でガードし、他プラットフォームでは「Android のみ対応」と表示。
- Windows の PowerShell で git commit -m “日本語” すると文字化けしたため、メッセージを UTF-8 のファイルに書いて git commit -F で読み込むようにした。
- パッケージ名変更時は MainActivity.kt の配置ディレクトリも work/mytiny/wasurena_week に合わせて移動。
こだわった箇所
週間カレンダーは「縦=日付・横=時間帯」
1 週間を一覧で見られるように、縦に月〜日、横に朝・昼・夕・寝る前の 4 コマで固定。時間帯は 4 つに絞ることで UI をシンプルにしました。○/✕/□ は「その時間帯の薬全体」の状態を 1 セルに集約しています。土曜は薄い青、日曜は薄い赤の行背景で週末が分かりやすくなるようにしました。
温かみのある配色と 0 件時のスタートタブ
背景は #FEF6E8、AppBar は #FFFBF5、文字は brown 系で統一。週間・薬・リマインダー・薬編集の全画面で同じトーンにしました。また、薬が 0 件のときは最初から「薬」タブを表示し、まず登録してもらう流れにしています。
データは端末内のみ・「記録」であること
サーバーは使わず、アカウントも不要。服薬は個人情報なので端末内の SQLite だけに保存しています。また、アプリは「記録」であり医療アドバイスは行わない旨を README に明記し、ストアのガイドラインにも配慮しました。
リマインダーは時間帯単位
薬ごとではなく「朝・昼・夕・寝る前」ごとに 1 時刻と ON/OFF を設定。薬が 1 つでも登録されている時間帯だけ通知するようにし、設定の手間を減らしました。
おわりに
勢いで(Cursor君が) 6 時間で形にしたアプリですが、スケジュールとログの一貫性や、リマインダーの届き方、配色や初回 UX までいくつかこだわれたと思います。リリースはしませんが、技術メモとして残しておきます。
