18 KiB
18 KiB
Antigravity向け - 新生ぽんるーむ実装プロンプト
実装日: 2025-12-29以降 実装担当: Antigravity サポート: Claude (Anthropic)
🚨 最重要指示
⛔ 絶対にやってはいけないこと
❌ 既存のWeb版(ponshu-room/lib/)からコードを1行もコピーしない
❌ 既存プロジェクトを編集しない
❌ Web版のUIを踏襲しない
✅ やるべきこと
✅ 完全に新しいFlutterプロジェクトを作成
✅ FINAL_REQUIREMENTS.mdに100%従う
✅ スキャンアプリ(mai_quick_scan)の成功パターンを参考にする
✅ 写真を主役にする
📱 プロジェクト作成(コピペして実行)
ステップ1: 新規プロジェクト作成
# 親ディレクトリへ移動
cd C:\Users\maita\posimai-project
# 完全に新しいFlutterプロジェクトを作成
flutter create ponshu_room_reborn
cd ponshu_room_reborn
ステップ2: pubspec.yaml編集
完全に置き換え:
name: ponshu_room_reborn
description: "Reborn Ponshu Room - My Digital Sake Cellar"
publish_to: 'none'
version: 2.0.0+1
environment:
sdk: ^3.10.1
dependencies:
flutter:
sdk: flutter
# 状態管理(スキャンアプリと同じ)
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解析
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
flutter:
uses-material-design: true
flutter pub get
ステップ3: Android設定
android/app/build.gradle.kts を編集:
android {
namespace = "com.posimai.ponshu_room"
compileSdk = 36 // Android 15+ 対応
defaultConfig {
applicationId = "com.posimai.ponshu_room"
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "2.0.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("debug")
}
}
}
🎨 デザイン仕様(最重要)
写真を主役にする「ナロー・マージン」設計
❌ 従来の余白設計(ダメな例)
ListView.separated(
padding: EdgeInsets.all(16), // ← 余白が大きすぎ
separatorBuilder: (context, index) => SizedBox(height: 16), // ← 隙間が大きすぎ
itemBuilder: (context, index) => SakeCard(...),
)
問題点: 写真が小さくなる、迫力がない
✅ 新生ぽんるーむの余白設計(正しい例)
ListView.separated(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 12), // ← 最小限
separatorBuilder: (context, index) => SizedBox(height: 4), // ← 細い隙間
itemBuilder: (context, index) => SakeCard(...),
)
メリット: 写真が大きい、迫力がある、Instagram的
カードデザイン - 「フル幅カード」
// lib/widgets/sake_card.dart
class SakeCard extends StatelessWidget {
final SakeItem item;
const SakeCard({required this.item});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Card(
margin: EdgeInsets.zero, // ← カード自体のマージンはゼロ
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0), // ← 角丸なし(フル幅)
side: BorderSide(
color: Color(0xFFE8E8E8),
width: 0.5, // ← 繊細なボーダー
),
),
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFE8E8E8),
width: 0.5,
),
),
),
child: Padding(
padding: EdgeInsets.all(12), // ← カード内の余白は最小限
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 左: 写真(できるだけ大きく)
ClipRRect(
borderRadius: BorderRadius.circular(8), // ← 写真だけ角丸
child: Image.network(
item.imagePath,
width: 120, // ← 大きく!
height: 120,
fit: BoxFit.cover,
),
),
SizedBox(width: 12),
// 右: テキスト情報
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 銘柄名(明朝体、大きく)
Text(
item.brandName ?? '日本酒',
style: GoogleFonts.notoSerifJp(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Color(0xFF1A1A1A),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4),
// 酒蔵・産地
Text(
'${item.breweryName ?? ''} | ${item.prefecture ?? ''}',
style: GoogleFonts.notoSansJp(
fontSize: 11,
color: Color(0xFF8A8A8A),
),
),
SizedBox(height: 8),
// 評価
if (item.rating != null)
Row(
children: List.generate(
5,
(index) => Icon(
index < item.rating!.round()
? Icons.star
: Icons.star_border,
size: 16,
color: Color(0xFFFFC107),
),
),
),
SizedBox(height: 4),
// 種類
if (item.type != null)
Text(
item.type!,
style: GoogleFonts.notoSansJp(
fontSize: 11,
color: Color(0xFF4A4A4A),
),
),
// キャッチコピー
if (item.catchCopy != null) ...[
SizedBox(height: 6),
Text(
item.catchCopy!,
style: GoogleFonts.notoSerifJp(
fontSize: 12,
fontStyle: FontStyle.italic,
color: Color(0xFF376495),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
),
);
}
}
レイアウトイメージ:
┌────────────────────────────────────┐
│ [120x120] │ 獺祭 ← 明朝体18px
│ 写真 │ 旭酒造 | 山口県 ← ゴシック体11px
│ 角丸8px │ ⭐⭐⭐⭐⭐ 純米大吟醸
│ │ "夜風と楽しみたい、淡麗な一滴"
└────────────────────────────────────┘
↑ 0.5pxのボーダーで区切り
🍶 Gemini 3.0統合(核心機能)
APIキー設定
// lib/secrets.dart
class Secrets {
static const String geminiApiKey = 'AIzaSyA2BSr16R2k0bHjSYcSUdmLoY8PKwaFts0';
}
リアルタイム実況付きGemini解析
// lib/services/gemini_service.dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:google_generative_ai/google_generative_ai.dart';
import 'dart:convert';
import '../secrets.dart';
class GeminiService {
late final GenerativeModel _model;
GeminiService() {
_model = GenerativeModel(
model: 'gemini-2.5-flash-latest', // スキャンアプリで実績あり
apiKey: Secrets.geminiApiKey,
generationConfig: GenerationConfig(
temperature: 0.1,
),
);
}
/// リアルタイム実況付きで解析
Stream<String> analyzeSakeLabelWithCommentary(Uint8List imageBytes) async* {
// ステージ1: 開始
yield 'ラベルを読んでいます...';
await Future.delayed(Duration(milliseconds: 500));
// ステージ2: 解析中
yield 'お酒の情報を確認しています...';
try {
final prompt = '''
この画像は日本酒のボトルまたはラベルの写真です。
ラベルに書かれているテキスト情報を正確に読み取り、JSON形式で返してください。
【重要な指示】
1. ラベルに明確に書かれている情報のみを抽出してください
2. 推測や想像で値を入れないでください(読めない場合はnullまたは省略)
3. 数値は必ず数字のみ(単位記号%などは除く)
4. このお酒の印象を一言で表す「キャッチコピー」を自動生成してください
【出力フォーマット】
{
"brandName": "銘柄名",
"type": "特定名称",
"alcoholContent": 数値,
"polishingRatio": 数値,
"breweryName": "酒蔵名",
"prefecture": "都道府県名",
"catchCopy": "夜風と楽しみたい、淡麗な一滴"
}
**JSON以外の余計な説明は一切不要です。JSONのみを出力してください。**
''';
final content = [
Content.multi([
TextPart(prompt),
DataPart('image/jpeg', imageBytes),
])
];
final response = await _model.generateContent(content);
final text = response.text ?? '';
// ステージ3: 都道府県を検出した場合
if (text.contains('県')) {
final prefectureMatch = RegExp(r'([^\s]+県)').firstMatch(text);
if (prefectureMatch != null) {
yield 'お、これは${prefectureMatch.group(1)}の銘柄ですね...';
await Future.delayed(Duration(milliseconds: 300));
}
}
// ステージ4: データ整理中
yield 'データを整理しています...';
await Future.delayed(Duration(milliseconds: 300));
// JSON抽出
final jsonMatch = RegExp(r'\{[\s\S]*\}').firstMatch(text);
if (jsonMatch != null) {
final jsonString = jsonMatch.group(0)!;
final data = jsonDecode(jsonString);
// ステージ5: 完了
yield '解析完了!';
// データを返す(特別な形式)
yield 'DATA:$jsonString';
} else {
yield 'エラー: データを読み取れませんでした';
}
} catch (e) {
yield 'エラー: $e';
}
}
/// シンプルな解析(実況なし)
Future<Map<String, dynamic>?> analyzeSakeLabel(Uint8List imageBytes) async {
try {
final prompt = '''
この画像は日本酒のボトルまたはラベルの写真です。
ラベルに書かれているテキスト情報を正確に読み取り、JSON形式で返してください。
【重要な指示】
1. ラベルに明確に書かれている情報のみを抽出してください
2. 推測や想像で値を入れないでください(読めない場合はnullまたは省略)
3. 数値は必ず数字のみ(単位記号%などは除く)
4. このお酒の印象を一言で表す「キャッチコピー」を自動生成してください
【出力フォーマット】
{
"brandName": "銘柄名",
"type": "特定名称",
"alcoholContent": 数値,
"polishingRatio": 数値,
"breweryName": "酒蔵名",
"prefecture": "都道府県名",
"catchCopy": "夜風と楽しみたい、淡麗な一滴"
}
**JSON以外の余計な説明は一切不要です。JSONのみを出力してください。**
''';
final content = [
Content.multi([
TextPart(prompt),
DataPart('image/jpeg', imageBytes),
])
];
final response = await _model.generateContent(content);
final text = response.text ?? '';
final jsonMatch = RegExp(r'\{[\s\S]*\}').firstMatch(text);
if (jsonMatch != null) {
final jsonString = jsonMatch.group(0)!;
return jsonDecode(jsonString);
}
return null;
} catch (e) {
debugPrint('Gemini API Error: $e');
return null;
}
}
}
リアルタイム実況の使用例
// lib/screens/input_screen.dart(一部)
StreamBuilder<String>(
stream: geminiService.analyzeSakeLabelWithCommentary(imageBytes),
builder: (context, snapshot) {
if (snapshot.hasData) {
final message = snapshot.data!;
// データが返ってきた場合
if (message.startsWith('DATA:')) {
final jsonString = message.substring(5);
final data = jsonDecode(jsonString);
// フォームに自動入力
_fillForm(data);
return ResultForm(data: data);
}
// 実況メッセージを表示
return AnalyzingOverlay(currentStage: message);
}
return AnalyzingOverlay(currentStage: 'カメラを起動しています...');
},
)
📦 データモデル
SakeItem (Hive Model)
// lib/models/sake_item.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;
@HiveField(17)
String? catchCopy; // AIが生成したキャッチコピー
@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,
});
}
# 生成コマンド
flutter pub run build_runner build
🚀 実装チェックリスト
Phase 1: MVP(5時間)
- プロジェクト作成(
flutter create ponshu_room_reborn) pubspec.yaml設定- Android設定(compileSdk: 36, targetSdk: 35)
lib/secrets.dart作成(APIキー)lib/models/sake_item.dart作成flutter pub run build_runner buildlib/theme/app_theme.dart作成(posimaiカラー)lib/main.dart作成(Hive初期化)lib/screens/home/home_screen.dart作成(4タブ)lib/services/gemini_service.dart作成(リアルタイム実況)lib/widgets/sake_card.dart作成(フル幅カード)lib/widgets/analyzing_overlay.dart作成(実況UI)- カメラ撮影機能
- 入力フォーム
- 詳細画面
- SafeArea対応 ← Android 15必須
Phase 2: 「美録」UI洗練(3時間)
- 余白調整(padding: 8dp, separator: 4dp)
- 明朝体×ゴシック体の適用
- Hero遷移
- Instagram用画像生成機能
Phase 3: 「遊び心」機能拡張(4時間)
- フレーバー・マトリックス
- 日本酒・制覇マップ + バッジ
- AIソムリエ(質問例、チャット)
- マイページ統計グラフ
Phase 4: 共有機能(2時間)
- シンプルテキスト共有
- Instagram用正方形画像生成
- キャッチコピー付き共有
✅ 完成基準
MVP完成の定義
- カメラで日本酒を撮影できる
- Gemini 3.0でリアルタイム実況しながら解析
- キャッチコピーが自動生成される
- データをHiveに保存できる
- フル幅カード(写真120x120)で表示
- 詳細表示できる
- SafeAreaで見切れない
- Android 15 (Xiaomi 14T Pro) で動作する
最終完成の定義
- すべての機能が動作
- フレーバー・マトリックス表示
- 日本酒・制覇マップ + バッジ
- Instagram用正方形画像生成
- 「雑誌のような」デザイン
- 60fpsの滑らかな動作
- 「魔法のような」心地よさ
📞 進捗報告
各フェーズ完了後に以下を報告してください:
Phase 1-1 完了: プロジェクト初期化
Phase 1-2 完了: データモデル
...
問題点:
- XXXでエラーが発生しました
- YYYの実装方法が不明です
質問:
- ZZZはどう実装すべきですか?
頑張ってください!「魔法のような体験」を一緒に作りましょう!🍶✨