# 新生ぽんるーむ - 最終完全要件定義書 **プロジェクト名**: 新生ぽんるーむ (Reborn Ponshu Room) **バージョン**: 2.0 - "My Digital Sake Cellar" **作成日**: 2025-12-29 **設計**: Claude (Anthropic) + Gemini (Google AI) + posimai **実装担当**: Antigravity + Claude --- ## 🎯 プロジェクトビジョン > **「目の前の一本を撮るだけで、魔法のようにデータが溜まっていく」** ### 3つの核心コンセプト #### 🍶 1. 「瞬撮」- 魔法の解析体験 カメラを向けて撮るだけで、AIが全てを読み取る。 ユーザーはただ「撮る」だけ。 #### 🎨 2. 「美録」- インスタ映えする情報の見せ方 雑誌のようなレイアウトで、いつでも見返したくなる。 そのままインスタに投稿できる美しさ。 #### 🧩 3. 「遊び心」- 意味のあるデータ分析 自分の好みを「発見」する楽しさ。 日本全国制覇マップ、フレーバーマトリックス。 --- ## 📱 技術スタック ### 必須要件 ```yaml Flutter SDK: 3.38.3+ Dart: 3.10.1+ Android: compileSdk: 36 targetSdk: 35 (Android 15対応) minSdk: 21 (Android 5.0+) ``` ### 依存パッケージ ```yaml dependencies: flutter: sdk: flutter # 状態管理(Riverpod - スキャンアプリで実績あり) flutter_riverpod: ^2.6.1 hooks_riverpod: ^2.6.1 flutter_hooks: ^0.20.5 # ローカルDB hive: ^2.2.3 hive_flutter: ^1.1.0 # AI解析(Gemini 3.0) google_generative_ai: ^0.4.7 http: ^1.6.0 # カメラ・画像 camera: ^0.11.0+2 image_picker: ^1.2.1 image: ^4.3.0 # UI google_fonts: ^6.2.1 fl_chart: ^1.1.1 # 共有・保存 share_plus: ^12.0.1 path_provider: ^2.1.5 # その他 intl: ^0.20.2 package_info_plus: ^9.0.0 url_launcher: ^6.3.2 countries_world_map: ^1.3.0 cupertino_icons: ^1.0.8 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 hive_generator: ^2.0.1 build_runner: ^2.4.13 ``` --- ## 🎨 UI/UXデザイン原則 ### デザインコンセプト **「雑誌のような洗練、魔法のような心地よさ」** ### カラーパレット ```dart // posimaiブランドカラー static const Color posimaiBlue = Color(0xFF376495); // ベースカラー static const Color warmOffWhite = Color(0xFFFAFAF9); // 背景 static const Color richBlack = Color(0xFF1A1A1A); // テキスト static const Color charcoalGray = Color(0xFF4A4A4A); // サブテキスト static const Color warmGray = Color(0xFF8A8A8A); // 補助テキスト // アクセントカラー static const Color dustyPink = Color(0xFFE8B4B8); // お気に入り static const Color softYellow = Color(0xFFFEF3C7); // ウィッシュリスト ``` ### タイポグラフィ ```dart // 銘柄名・タイトル: 明朝体(格調高く) headlineMedium: GoogleFonts.notoSerifJp( fontSize: 20, fontWeight: FontWeight.w600, letterSpacing: 0.5, color: richBlack, ) // データ・ボディ: ゴシック体(読みやすく) bodyMedium: GoogleFonts.notoSansJp( fontSize: 13, fontWeight: FontWeight.w400, color: charcoalGray, ) // ラベル・キャプション: ゴシック体 bodySmall: GoogleFonts.notoSansJp( fontSize: 11, fontWeight: FontWeight.w500, color: warmGray, ) ``` --- ## 🍶 1. 「瞬撮」- 魔法の解析体験 ### APIキー設定 ```dart // lib/secrets.dart class Secrets { static const String geminiApiKey = 'AIzaSyA2BSr16R2k0bHjSYcSUdmLoY8PKwaFts0'; } ``` ### Gemini 3.0統合 **重要**: 以下のいずれかを使用 ```dart // オプション1: 最新の3.0プレビュー(推奨) model: 'gemini-3.0-flash-latest' // オプション2: 安定版2.5 Flash(スキャンアプリで実績) model: 'gemini-2.5-flash-latest' ``` ### AIソムリエのリアルタイム実況 **解析中のUI**: ```dart class AnalyzingOverlay extends StatelessWidget { final String currentStage; const AnalyzingOverlay({required this.currentStage}); @override Widget build(BuildContext context) { return Container( color: Colors.black.withOpacity(0.7), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(posimaiBlue), ), SizedBox(height: 24), Text( currentStage, style: GoogleFonts.notoSansJp( fontSize: 16, color: Colors.white, ), ), ], ), ), ); } } ``` **ステージの例**: ```dart [ 'ラベルを読んでいます...', 'お、これは〇〇県の銘柄ですね...', '精米歩合を確認中...', 'データを整理しています...', ] ``` ### 自動ポエム生成 Geminiのレスポンスに以下を追加: ```dart final prompt = ''' この画像は日本酒のボトルまたはラベルの写真です。 ラベルに書かれているテキスト情報を正確に読み取り、JSON形式で返してください。 【重要な指示】 1. ラベルに明確に書かれている情報のみを抽出してください 2. 推測や想像で値を入れないでください(読めない場合はnullまたは省略) 3. 数値は必ず数字のみ(単位記号%などは除く) 4. このお酒の印象を一言で表す「キャッチコピー」を自動生成してください 【出力フォーマット】 { "brandName": "銘柄名", "type": "特定名称", "alcoholContent": 数値, "polishingRatio": 数値, "breweryName": "酒蔵名", "prefecture": "都道府県名", "catchCopy": "夜風と楽しみたい、淡麗な一滴" // ← 自動生成 } **JSON以外の余計な説明は一切不要です。JSONのみを出力してください。** '''; ``` --- ## 🎨 2. 「美録」- インスタ映えする情報の見せ方 ### モダン・カタログ・カード **Web版の縦並びを捨て、左右2段構成を採用**: ```dart // lib/widgets/sake_card.dart class SakeCard extends StatelessWidget { final SakeItem item; const SakeCard({required this.item}); @override Widget build(BuildContext context) { return Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: Color(0xFFE8E8E8), width: 1), ), child: Padding( padding: EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 左: 写真(角丸12px) ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( item.imagePath, width: 100, height: 100, fit: BoxFit.cover, ), ), SizedBox(width: 16), // 右: テキスト情報 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 銘柄名(明朝体、大きく) Text( item.brandName ?? '日本酒', style: GoogleFonts.notoSerifJp( fontSize: 18, fontWeight: FontWeight.w600, color: richBlack, ), ), SizedBox(height: 4), // 酒蔵・産地(ゴシック体、控えめ) Text( '${item.breweryName ?? ''} | ${item.prefecture ?? ''}', style: GoogleFonts.notoSansJp( fontSize: 11, color: warmGray, ), ), SizedBox(height: 8), // 評価 StarRating(rating: item.rating ?? 0), SizedBox(height: 4), // 種類 Text( item.type ?? '', style: GoogleFonts.notoSansJp( fontSize: 11, color: charcoalGray, ), ), // キャッチコピー(NEW!) if (item.catchCopy != null) ...[ SizedBox(height: 8), Text( item.catchCopy!, style: GoogleFonts.notoSerifJp( fontSize: 12, fontStyle: FontStyle.italic, color: posimaiBlue, ), ), ], ], ), ), ], ), ), ); } } ``` ### インスタ専用・共有カード生成 **正方形(1:1)の画像を自動生成**: ```dart // lib/services/instagram_share_service.dart import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:image/image.dart' as img; class InstagramShareService { static Future generateInstagramImage(SakeItem item) async { // 1. 画像を読み込み final imageBytes = await File(item.imagePath).readAsBytes(); final image = img.decodeImage(imageBytes)!; // 2. 正方形(1080x1080)にクロップ final size = 1080; final square = img.copyResizeCropSquare(image, size: size); // 3. 下半分にposimaiカラーのオーバーレイ final overlay = img.Image(width: size, height: size ~/ 2); img.fillRect( overlay, x1: 0, y1: 0, x2: size, y2: size ~/ 2, color: img.ColorRgb8(55, 100, 149), // posimaiBlue ); // 4. オーバーレイを合成 img.compositeImage( square, overlay, dstX: 0, dstY: size ~/ 2, ); // 5. テキストを描画(銘柄名、評価、ハッシュタグ) // TODO: 白抜き明朝体でテキスト描画 // 6. 保存 final directory = await getTemporaryDirectory(); final path = '${directory.path}/instagram_${DateTime.now().millisecondsSinceEpoch}.jpg'; await File(path).writeAsBytes(img.encodeJpg(square)); return path; } static Future shareToInstagram(SakeItem item) async { // インスタ用画像を生成 final imagePath = await generateInstagramImage(item); // 共有 await Share.shareXFiles( [XFile(imagePath)], text: ''' 🍶 ${item.brandName ?? '日本酒'} ${item.catchCopy ?? ''} #ぽんるーむ #日本酒 #${item.prefecture ?? ''} #日本酒好きと繋がりたい ''', ); } } ``` **レイアウトイメージ**: ``` ┌────────────────────┐ │ │ │ [日本酒ラベル写真] │ ← 上半分(540px) │ │ ├────────────────────┤ │ posimaiブルー背景 │ ← 下半分(540px) │ │ │ 獺祭(明朝体・白) │ ← 銘柄名 │ ⭐⭐⭐⭐⭐ │ ← 評価 │ │ │ 夜風と楽しみたい、 │ ← キャッチコピー │ 淡麗な一滴 │ │ │ │ #ぽんるーむ │ ← ハッシュタグ │ [logo] │ ← posimaiロゴ(右下) └────────────────────┘ ``` --- ## 🧩 3. 「遊び心」- 意味のあるデータ分析 ### フレーバー・マトリックス **4象限チャートで味の傾向を可視化**: ``` 甘口 ↑ │ 濃醇 ←─┼─→ 淡麗 │ ↓ 辛口 ``` **実装**: ```dart // lib/widgets/flavor_matrix.dart class FlavorMatrix extends StatelessWidget { final List items; const FlavorMatrix({required this.items}); @override Widget build(BuildContext context) { // AIが解析したフレーバータグから傾向を計算 // 例: "甘口"タグが多い → 甘口寄り // "フルーティー"タグが多い → 淡麗寄り return Container( height: 200, child: CustomPaint( painter: FlavorMatrixPainter( userPosition: _calculateUserPosition(), ), ), ); } Offset _calculateUserPosition() { // ユーザーの好みを計算 // 甘口/辛口、濃醇/淡麗の2軸で位置を決定 return Offset(0.3, 0.6); // 例: やや甘口、やや淡麗 } } ``` ### 日本酒・制覇マップ **都道府県マップをposimaiカラーで塗りつぶし**: ```dart // lib/screens/home/map_tab.dart SimpleMap( instructions: SMapJapan.instructions, defaultColor: warmGray, // 未踏破 colors: SMapJapanColors( // データから自動計算 ..._prefectureColors(), ).toMap(), callback: (id, name, tapDetails) { // タップで詳細表示 _showPrefectureDetail(name); }, ) Map _prefectureColors() { final counts = _countByPrefecture(); return counts.map((prefecture, count) { if (count == 0) return MapEntry(prefecture, warmGray); if (count < 3) return MapEntry(prefecture, posimaiBlue.withOpacity(0.3)); return MapEntry(prefecture, posimaiBlue); }); } ``` **バッジ表示**: ```dart // マップの上部に表示 Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Text( '制覇: ${_completedPrefectures()} / 47', style: GoogleFonts.notoSerifJp( fontSize: 24, fontWeight: FontWeight.bold, color: posimaiBlue, ), ), SizedBox(height: 8), Text( 'あと${47 - _completedPrefectures()}県で全国制覇!', style: GoogleFonts.notoSansJp( fontSize: 13, color: charcoalGray, ), ), ], ), ) ``` ### Synology バックアップ・ステータス **マイページの右上に控えめに表示**: ```dart // lib/screens/home/profile_tab.dart Positioned( top: 16, right: 16, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.green[50], borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.green[200]!), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.cloud_done, size: 16, color: Colors.green[700]), SizedBox(width: 4), Text( 'Home Lab Sync: OK', style: GoogleFonts.notoSansJp( fontSize: 11, color: Colors.green[700], fontWeight: FontWeight.w500, ), ), ], ), ), ) ``` --- ## 📦 データモデル(更新版) ### SakeItem (Hive Model) **キャッチコピーを追加**: ```dart import 'package:hive/hive.dart'; part 'sake_item.g.dart'; @HiveType(typeId: 0) class SakeItem extends HiveObject { // 基本情報 @HiveField(0) String? brandName; @HiveField(1) String? breweryName; @HiveField(2) String? prefecture; @HiveField(3) String? type; @HiveField(4) double? alcoholContent; @HiveField(5) int? polishingRatio; // 画像 @HiveField(6) String imagePath; @HiveField(7) List? additionalImages; // 評価・メモ @HiveField(8) double? rating; @HiveField(9) String? memo; @HiveField(10) List? tags; // フラグ @HiveField(11) bool isFavorite; @HiveField(12) bool isWishlist; // 日時 @HiveField(13) DateTime createdAt; @HiveField(14) DateTime? updatedAt; // 価格(オプション) @HiveField(15) int? price; @HiveField(16) int? volume; // 🆕 AIが生成したキャッチコピー @HiveField(17) String? catchCopy; // 🆕 フレーバープロファイル(甘口/辛口、濃醇/淡麗) @HiveField(18) double? sweetnessScore; // -1.0(辛口)~ 1.0(甘口) @HiveField(19) double? bodyScore; // -1.0(淡麗)~ 1.0(濃醇) SakeItem({ this.brandName, this.breweryName, this.prefecture, this.type, this.alcoholContent, this.polishingRatio, required this.imagePath, this.additionalImages, this.rating, this.memo, this.tags, this.isFavorite = false, this.isWishlist = false, required this.createdAt, this.updatedAt, this.price, this.volume, this.catchCopy, this.sweetnessScore, this.bodyScore, }); } ``` --- ## 🚀 実装優先順位 ### Phase 1: MVP(5時間) #### チェックリスト - [ ] プロジェクト初期化(`flutter create ponshu_room_reborn`) - [ ] Android設定(compileSdk: 36, targetSdk: 35) - [ ] 依存関係追加 - [ ] Hiveセットアップ - [ ] SakeItemモデル(キャッチコピー含む) - [ ] posimaiテーマ - [ ] ホーム画面骨組み(4タブ) - [ ] **Gemini 3.0解析 + リアルタイム実況** - [ ] カメラ撮影 - [ ] 入力フォーム - [ ] 詳細画面 - [ ] SafeArea対応 ### Phase 2: 「美録」UI洗練(3時間) #### チェックリスト - [ ] モダン・カタログ・カード(左右2段構成) - [ ] 明朝体×ゴシック体の適用 - [ ] インスタ専用画像生成機能 - [ ] Hero遷移 - [ ] アニメーション(200-300ms) ### Phase 3: 「遊び心」機能拡張(4時間) #### チェックリスト - [ ] フレーバー・マトリックス - [ ] 日本酒・制覇マップ + バッジ - [ ] Synologyバックアップ・ステータス表示 - [ ] AIソムリエ(質問例、チャット形式) - [ ] マイページ統計グラフ - [ ] 検索・フィルタ・ソート ### Phase 4: 共有機能(2時間) #### チェックリスト - [ ] シンプルテキスト共有 - [ ] Instagram用正方形画像生成 - [ ] キャッチコピー付き共有 **合計所要時間**: 14時間 --- ## 📐 画面構成 ### ホーム画面(HomeScreen) #### ボトムナビゲーション ``` ┌─────┬─────┬─────┬─────┐ │ 🍶 │ 🗺️ │ 🤖 │ 👤 │ │ 酒 │ マップ│ AI │ MY │ └─────┴─────┴─────┴─────┘ ``` #### タブ1: 酒リスト(ListTab) **モダン・カタログ・カード**: ``` ┌────────────────────────────┐ │ [100x100] │ 獺祭 │ ← 明朝体、大きく │ 写真 │ 旭酒造 | 山口県 │ ← ゴシック体、控えめ │ 角丸12px │ ⭐⭐⭐⭐⭐ 純米大吟醸 │ │ │ "夜風と楽しみたい、淡麗な一滴" │ ← キャッチコピー └────────────────────────────┘ ``` #### タブ2: マップ(MapTab) **日本酒・制覇マップ**: - 未踏破: warmGray - 3本未満: posimaiBlue (30% opacity) - 3本以上: posimaiBlue (100%) - バッジ: 「制覇: 5 / 47」「あと42県で全国制覇!」 #### タブ3: AIソムリエ(AiTab) **質問例ボタン**: ```dart Wrap( spacing: 8, children: [ OutlinedButton( child: Text('純米大吟醸とは?'), onPressed: () => _askAI('純米大吟醸について詳しく教えて'), ), OutlinedButton( child: Text('山田錦について教えて'), onPressed: () => _askAI('山田錦という酒米の特徴を教えて'), ), OutlinedButton( child: Text('刺身に合う日本酒は?'), onPressed: () => _askAI('刺身に合う日本酒のおすすめを教えて'), ), OutlinedButton( child: Text('初心者におすすめの銘柄'), onPressed: () => _askAI('日本酒初心者におすすめの銘柄を教えて'), ), ], ) ``` #### タブ4: マイページ(ProfileTab) **セクション構成**: 1. **Synologyバックアップステータス**(右上) ``` 🏠 Home Lab Sync: OK ``` 2. **酒蔵サマリー** ``` ┌─────────┬─────────┬─────────┐ │ 1 │ 0 │ 0 │ │ 飲んだ本数│お気に入り│ 買いたい │ └─────────┴─────────┴─────────┘ ``` 3. **フレーバー・マトリックス** ``` 甘口 ↑ │ ●(あなた) 濃醇 ←─┼─→ 淡麗 │ ↓ 辛口 あなたが選ぶ酒は、フルーティーな甘口に偏っています ``` 4. **よく飲む都道府県** ``` 🥇 青森県 ━━━━━━━━━━ 1本 ``` 5. **飲酒傾向グラフ** - 月別飲酒本数(直近6ヶ月) - 評価分布(1-5星) --- ## 🔐 プライバシー・セキュリティ ### データ保存場所 #### デフォルト(全ユーザー) ``` ✅ ローカル(Hive DB)のみ ✅ 写真はアプリ専用ディレクトリ ❌ 外部サーバーへの送信なし ``` #### オプション(posimai専用) ``` ✅ Synology NAS連携(WebDAV/FTP) ✅ 自動バックアップ ✅ "Home Lab Sync: OK" ステータス表示 ``` --- ## 📄 ファイル構成 ``` lib/ ├── main.dart ├── secrets.dart ├── models/ │ ├── sake_item.dart │ └── sake_item.g.dart ├── providers/ │ ├── sake_repository_provider.dart │ ├── gemini_provider.dart │ └── camera_provider.dart ├── services/ │ ├── hive_service.dart │ ├── gemini_service.dart │ ├── share_service.dart │ └── instagram_share_service.dart ← 🆕 ├── screens/ │ ├── home/ │ │ ├── home_screen.dart │ │ ├── list_tab.dart │ │ ├── map_tab.dart │ │ ├── ai_tab.dart │ │ └── profile_tab.dart │ ├── detail_screen.dart │ └── input_screen.dart ├── widgets/ │ ├── sake_card.dart ← 🆕 左右2段構成 │ ├── star_rating.dart │ ├── prefecture_dropdown.dart │ ├── tag_chip.dart │ ├── flavor_matrix.dart ← 🆕 │ └── analyzing_overlay.dart ← 🆕 リアルタイム実況 └── theme/ └── app_theme.dart ``` --- ## ✅ 完成基準 ### MVP完成の定義 - [ ] カメラで日本酒を撮影できる - [ ] Gemini 3.0でリアルタイム実況しながら解析 - [ ] キャッチコピーが自動生成される - [ ] データをHiveに保存できる - [ ] モダン・カタログ・カード(左右2段)で表示 - [ ] 詳細表示できる - [ ] SafeAreaで見切れない - [ ] Android 15 (Xiaomi 14T Pro) で動作する ### 最終完成の定義 - [ ] すべての機能が動作 - [ ] フレーバー・マトリックス表示 - [ ] 日本酒・制覇マップ + バッジ - [ ] Instagram用正方形画像生成 - [ ] Synologyバックアップステータス表示 - [ ] 「雑誌のような」デザイン - [ ] 60fpsの滑らかな動作 - [ ] 「魔法のような」心地よさ --- **最終更新**: 2025-12-29 **設計**: Claude (Anthropic) + Gemini (Google AI) + posimai **実装担当**: Antigravity + Claude **Let's create the magic! 🍶✨**