ponshu-room-lite/FINAL_REQUIREMENTS.md

924 lines
23 KiB
Markdown
Raw Normal View History

# 新生ぽんるーむ - 最終完全要件定義書
**プロジェクト名**: 新生ぽんるーむ (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<String> 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<void> 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<SakeItem> 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<String, Color> _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<String>? additionalImages;
// 評価・メモ
@HiveField(8)
double? rating;
@HiveField(9)
String? memo;
@HiveField(10)
List<String>? 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: MVP5時間
#### チェックリスト
- [ ] プロジェクト初期化(`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! 🍶✨**