924 lines
23 KiB
Markdown
924 lines
23 KiB
Markdown
|
|
# 新生ぽんるーむ - 最終完全要件定義書
|
|||
|
|
|
|||
|
|
**プロジェクト名**: 新生ぽんるーむ (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: 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! 🍶✨**
|