はじめに
以下の文章は、生成aiに作らせたものをコピペしただけです。
構成は友人のブログページ @tama0931 のマークダウン形式出力をコピーしたものを、Google ai studioで現在開発しているセクションに「こんな感じのやつ作って」と生成させたものです。許可はちゃんと取ってます!優しい!チュキ!ここで色々語るのもうるさいので早速本題に入ります!
⇩⇩⇩
週末、趣味の手回し焙煎に没頭していた、しがない個人開発者です。
今回は、私が「自分が本当に欲しいもの」を追い求めて開発している、AI駆動型 手回し焙煎ログアプリの、設計思想とアーキテクチャの全てを、ここに記録します。
「今日の焙煎、なぜか、めちゃくちゃ上手くいったぞ…?」
「昨日の失敗、もう二度と繰り返したくない…」
そんな、手回し焙煎家が必ずぶつかる、再現不能な「感覚の壁」。
その壁を、データとAIの力で打ち破り、全ての焙煎を「再現可能な科学」と「最高の楽しさ」に変える。
そんな夢のようなアプリの、**「骨格」と「神経網」**が、どう作られているのか。
この記事が、これからFlutterやFirebaseで、AIを相棒に、何かを作ろうとしている、誰か一人の「北極星」になれば、これほど嬉しいことはありません。
✅ 今回の達成内容
- 焙煎ログのCRUD (Create, Read, Update, Delete) 基本機能の実装
- AI (Gemini API) との連携 による、焙煎データ分析&フィードバック機能
- リアルタイムUI更新 (StreamBuilder)
- 洗練された入力体験 (DatePicker, モーダルフォーム)
🛠️ 技術スタック
- フレームワーク : Flutter
- 言語: Dart
- データベース : Cloud Firestore (Firebase)
- UI: Material 3
- 開発パートナー : Gemini API 🤖
- バージョン管理 : Git / GitHub
📋 システム設計の考え方
「記録」を「気づき」に変える、魔法のサイクル
このアプリの心臓部は、「ユーザーの体験を、可能な限りシンプルに、そしてリッチにする」という思想に基づいています。ユーザーはただ焙煎の記録を入力するだけ。すると、バックエンドでAIが働き、ユーザーに新たな「気づき」を与えてくれる。この魔法のような体験を、低コストかつ高速に実現するため、FlutterとFirebase、そしてGemini APIを中心とした、モダンなサーバーレスアーキテクチャを選択しました。
🗄️ データベース設計の考慮点
シンプルさと拡張性を両立する、Firestore設計
データベースには、ドキュメント指向で柔軟性の高いCloud Firestoreを採用しました。MVP段階では、複雑なリレーションを避け、直感的で拡張しやすい構造を最優先しています。
(注: 現状の実装では、USERSテーブルはまだなく、ROAST_LOGSをトップレベルコレクションとして扱っています。上記は将来的な拡張を見越した理想形です)
🎯 設計のポイント
-
非正規化の許容: まずは
roastLogsコレクション一つでスタート。ユーザー認証導入後も、user_idフィールドを追加するだけで、スケーラブルなデータ管理が可能です。 -
時系列データの重視: ユーザーが入力する
roastDateとは別に、ソート用のcreatedAtをサーバータイムスタンプで記録。これにより、データの整合性と柔軟なクエリを両立します。 -
AIフィードバックの永続化:
aiFeedbackフィールドを設けることで、一度生成したAIからの貴重なアドバイスを永続化。APIコストを削減しつつ、ユーザーがいつでも過去の「物語」を振り返れるようにします。
🔧 実装アーキテクチャ
AI連携とリアクティブUIの融合
このアプリの「魔法」を実現する、二つの重要な実装パターンについて解説します。
1. AI連携の非同期UX
ユーザーが「保存」ボタンを押してから、UIが固まることなく、裏側でFirestoreへの保存とGemini APIへのリクエストを処理する。この快適なUXを実現するために、Future.wait(あるいは、それに準ずる直列処理)と、ローディング表示を組み合わせたアーキテクチャを採用しています。
// add_roast_log_form.dart の _saveLog 関数の概念
Future<void> saveLog() async {
// 1. ユーザーを待たせない、ローディングUIを表示
showLoadingDialog();
try {
final logData = getFormDataFromUI();
// 2. まず、AIに分析を依頼し、フィードバックを取得
final aiFeedback = await aiService.getRoastFeedback(logData);
// 3. AIのフィードバックも含めて、完全なデータをFirestoreに保存
final finalLogData = { ...logData, 'aiFeedback': aiFeedback };
await firestore.collection("roastLogs").add(finalLogData);
// 4. 全てが完了したら、ローディングを閉じ、成功を伝える
closeLoadingDialog();
showSuccessSnackBar("AIの分析が完了しました!");
} catch (error) {
// 5. エラー時も、必ずローディングを閉じ、原因を伝える
closeLoadingDialog();
showErrorSnackBar(error);
}
}
2. Firestore StreamBuilderによるリアクティブUI
データの表示には、Firestoreの**StreamBuilder**を全面的に採用。これにより、データが変更(追加、更新、削除)されると、アプリのUIが、何もしなくても自動的に、リアルタイムで再描画されるという、Flutterの最も強力な機能の一つを、最大限に活用しています。
😤 つまずいたポイントと解決策
1. 3584個のXcode警告と、「ゴースト」ファイル
問題: flutter cleanしても、pod installしても、Xcodeでビルドすると、3584個という、天文学的な数の警告が発生。
Stale file '.../cloud_firestore.framework' is located outside of the allowed root paths.
という、不気味なメッセージが、その原因でした。
解決策: これは、過去のビルドの失敗により、プロジェクト内に古い、腐敗した「幽霊」のようなキャッシュファイルが、不法に存在していたことが原因でした。
最終的に、以下の**「ゴースト・バスターズ作戦」**によって、全ての幽霊を、完全に退治しました。
# プロジェクトを、物理的に、浄化する
flutter clean
cd ios
rm -rf Podfile.lock Pods .symlinks Runner.xcworkspace
pod cache clean --all # Mac全体のキャッシュをも掃除する、最終兵器
cd ..
flutter pub get
# 最後に、アーキテクチャの壁を超える、魔法の呪文
cd ios
arch -x86_64 pod install
2. 「沈黙のカレンダー」と、非同期UIの罠
問題: DatePickerを呼び出すボタンをタップしても、エラーも出ず、何も起きないという、最も厄介な「沈黙の壁」に、何度も、何度も、ぶつかりました。
解決策: この原因は、一つではありませんでした。
pubspec.yamlへのintlとflutter_localizationsの追加漏れmain.dartでの、国際化対応設定の不足- 非同期処理の谷間を越えて
BuildContextを使おうとしたことによる、デバッガーのハングアップ
これら、全ての「罠」を、一つずつ、デバッグログを仕掛け、原因を特定し、乗り越えることで、ついに、美しい日本語化されたカレンダーを、表示させることができました。
💡 学んだこと・Tips
Flutter × Firebase iOS開発の「お作法」
-
podは、友達。でも、時々、反乱する。: iOSのビルドで、原因不明のエラーが出たら、まずiosフォルダ内のPodfile.lock,Pods,Runner.xcworkspaceを削除し、pod installし直すのが、最も確実な「お清め」である。 -
M1/M2/M3 Macは、「翻訳機」を意識する: Apple Silicon搭載のMacで
pod installが失敗する場合、arch -x86_64 pod installという「魔法の呪文」が、アーキテクチャの壁を破壊してくれることがある。 -
非同期の「谷間」を、信じるな:
asyncを伴う処理の後で、UIを操作(NavigatorやScaffoldMessenger)する場合は、必ず、awaitの**「前」**に、contextから必要なものを変数に保存しておく。これが、非同期UIの「黄金律」である。 -
pubspec.yamlは、全ての「設計図」: 新しいパッケージを使いたい時は、まず、この「持ち物リスト」に、正しく、宣言する。全ての依存関係は、ここから、始まる。
おわりに
この数日間は、まさに「地獄」でした。
しかし、その果てに、「AIが、私の焙煎に、初めて、言葉を与えてくれた」、あの瞬間の感動は、一生、忘れることはないでしょう。
この記事が、これからFlutterとFirebase、そしてAIという、エキサイティングな世界に飛び込もうとしている、誰か一人の、小さな「勇気」や「道しるべ」になれば、これほど嬉しいことはありません。
最後まで読んでいただき、本当に、ありがとうございました!
いいね・ストック・コメントで、この、しがない個人開発者を応援していただけると、焙煎する豆が、一つ、グレードアップします🚀
#Flutter #Firebase #Gemini #AI #個人開発 #アプリ開発 #Qiita