Skip to content

nex50792/flutter-monemory

Repository files navigation

flutter-monemory

家計簿と AI チャット、領収書のカメラ読み取りを 1 つにまとめたモバイルアプリ。Web 版 monemory の Flutter 派生で、iOS と Android の両方で動く。

ホーム 統計 チャット 領収書スキャン 設定 ランチャー iOS
home stats chat ocr settings launcher ios

使ったもの

  • Flutter 3.44.4 / Dart 3.12.2
  • Riverpod 2 系(状態管理)
  • go_router(画面遷移)
  • Supabase Flutter(認証)
  • Gemini 2.5 Flash(チャット応答と領収書の画像解析)
  • sqflite(ローカルの SQLite 保存)
  • flutter_local_notifications(毎日のリマインダー、FCM は使わない)
  • local_auth(Face ID / Touch ID / 指紋によるアプリロック)
  • home_widget(Android のホーム画面ウィジェット)

対象端末は Android(Pixel 8 エミュレータで確認)、iOS(iPhone 17 シミュレータで確認)、Web ビルド。

Web 版との使い分け

Web とモバイルで同じ機能を無理に揃えず、それぞれの得意分野に絞る。

  • Web は音声入力とダッシュボードが強く、腰を据えて使う想定
  • モバイルはカメラ、通知、ホーム画面ウィジェット、生体認証で「片手・数秒」の使い勝手に寄せる

Gemini の画像・音声入力 (マルチモーダル) は Web で音声、モバイルで画像に振り分けて、どちらも 1 回の API 呼び出しで済ませる形にした。

できること

記録の管理 今月の合計とカテゴリ別の内訳をホームで確認できる。金額とカテゴリ、メモを入力して 1 タップで追加、スワイプで削除。SQLite に保存されるのでアプリを閉じても残る。

AI チャット Gemini に記録一覧をそのまま渡し、家計についての質問に答えてもらう。「今月の合計は?」と聞けば「¥11,350 です」と返してくる。RAG(pgvector)は使っておらず、記録数が少ないうちは全件を JSON で渡してしまう方が早くて正確。

領収書スキャン カメラかギャラリーからレシート画像を選ぶと、Gemini が画像ごと読み取って金額・カテゴリ・店名を決まった形の JSON で返す。実際にセブンイレブンのレシートで試したところ、合計 ¥658・食費・店名まで正しく取れた。

毎日 22 時のリマインダー 「今日の記録忘れてない?」を端末内でスケジュールする。Firebase Cloud Messaging を使わずに flutter_local_notifications で完結、サーバー不要。設定でトグルすると Android は AlarmManager、iOS は UNNotification に登録される。

生体認証でロック 設定でオンにすると、起動時とバックグラウンド復帰時に Face ID / Touch ID / 指紋 を要求する。認証状態は端末内の SharedPreferences に保存。

Android ホーム画面ウィジェット 今月の支出額と記録件数を表示するウィジェットを Android のホームに置ける。Kotlin で書いた AppWidgetProvider が Flutter 側の SharedPreferences(home_widget プラグイン経由)を読んで RemoteViews を更新する。長押しでウィジェット一覧を開くと「monemory」で検索できる。

Supabase Auth メールアドレスを入れて 6 桁のコードでログインする方式。実際に送信できるところまで確認済み(送信数の上限が 1 時間 4 通なので、それ以上試すには独自の SMTP 設定が要る)。

API キーが無いときの振る舞い

GEMINI_API_KEY を渡さずに起動しても落ちない。OCR は「MOCK」バッジ付きのダミー応答、チャットは「(MOCK) 記録は 5 件、合計 ¥11,350 です」を返す。Supabase の URL / anon key も同様で、未設定なら Login 画面に注意書きが出るだけ。

これによって、手元のリポジトリだけで起動して UI と機能フローを一通り試せる。

動作確認したところ

  • レシート画像から Gemini が ¥658 / 食費 / セブンイレブン を抽出(Android Pixel 8 エミュレータ)
  • チャットに記録一覧を渡して「What is my total spending this month?」→「今月の合計支出は¥11,350です。」(Android)
  • 22:00 のローカル通知が adb shell dumpsys alarm で RTC_WAKEUP に登録済み
  • Supabase Auth の 6 桁コード送信でレート上限 429 に到達(実接続している証拠)
  • Android のリリースビルドで INTERNET permission が debug と profile の manifest にしか無い罠を発見・修正
  • iPhone 17 シミュレータ(iOS 26.5)で debug build 起動、Supabase init のログを確認

開発

環境変数は env.json にまとめて --dart-define-from-file で渡す。env.json はリポジトリに入れない。

cp env.json.example env.json
# SUPABASE_URL / SUPABASE_ANON_KEY / GEMINI_API_KEY を書き込む

Makefile 経由で日常的な操作を行う。

make help          # ターゲット一覧
make install       # 依存取得
make test          # ユニットとウィジェットの全テスト
make analyze       # 静的解析
make up            # Android エミュレータ起動
make run-android   # Android にデプロイ
make run-ios       # iOS シミュレータにデプロイ
make run-web       # Chrome で起動
make build-apk     # Android リリースビルド
make build-ios     # iOS リリースビルド(署名なし)
make build-web     # Web リリースビルド
make icons         # アイコンを全プラットフォームへ展開
make splash        # スプラッシュを全プラットフォームへ展開
make doctor        # flutter doctor
make clean         # ビルド成果物の削除

テスト

make test で以下がまとめて走る。

  • dao_test – 抽象化した EntryDao の insert / findAll / deleteById / count を InMemory 実装で境界確認
  • format_test – 円表示と相対日時の閾値
  • chat_service_test – Gemini キー未設定時の mock 応答
  • entries_provider_test – 今月合計とカテゴリ別集計
  • widget_test – ホーム画面と下部ナビゲーションの存在

CI

.github/workflows/ci.yml で 4 ジョブを並列に走らせる。

  • test(ubuntu): analyze と全テスト
  • build-web(ubuntu): Web リリースビルド
  • build-android(ubuntu): APK リリースビルド
  • build-ios(macos-15): iOS リリースビルド(署名なし)

Flutter のバージョンは FLUTTER_VERSION env で一元管理。

リポジトリ構成

flutter-monemory/
├── lib/
│   ├── main.dart
│   ├── app.dart
│   ├── core/
│   │   ├── env.dart               # --dart-define で注入する env
│   │   ├── router.dart            # go_router (ShellRoute + 6 画面)
│   │   └── theme.dart
│   ├── shared/
│   │   ├── models/entry.dart
│   │   ├── providers/entries_provider.dart
│   │   ├── db/entry_dao.dart      # sqflite + InMemory fallback
│   │   └── format.dart
│   └── features/
│       ├── shell/                 # BottomNavigation shell
│       ├── home/
│       ├── entries/               # 記録追加フォーム
│       ├── chat/                  # Gemini に記録一覧を渡すチャット
│       ├── stats/
│       ├── settings/
│       ├── auth/                  # Supabase 6 桁 OTP
│       ├── receipt/               # Gemini OCR
│       ├── notifications/         # 毎日 22:00 リマインダー
│       ├── biometric/             # local_auth によるアプリロック
│       └── widget/                # Android ホームウィジェット同期
├── test/
├── assets/icon/icon.png
├── android/                       # AndroidManifest, MonemoryWidgetProvider.kt, widget layout
├── ios/                           # Info.plist (permission / URL scheme)
├── web/
├── docs/screenshots/
├── Makefile
├── flutter_launcher_icons.yaml
├── flutter_native_splash.yaml
└── .github/workflows/ci.yml

About

No description or website provided.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors