feat: Add Pro version lock mechanism for Lite version preparation
Phase 4.1完了: Pro版機能ロック機構の実装
変更内容:
- lib/widgets/pro_locked_screen.dart: 新規作成
- 和モダンな王冠アイコン付きロック画面
- Pro版限定機能を分かりやすく表示
- 日英両対応
- lib/main.dart: IS_PRO_VERSION ビルドフラグ追加
- bool.fromEnvironment('IS_PRO_VERSION', defaultValue: true)
- Pro版/Lite版をビルド時に切り替え可能に
- lib/screens/main_screen.dart: 条件分岐ロジック追加
- パーソナルモード: QRスキャン → Lite版ではロック画面
- ビジネスモード: Instagram・分析 → Lite版ではロック画面
- タブ表示は維持、タップ時にロック画面を表示
ビルドコマンド:
- Pro版: flutter build apk --release --dart-define=IS_PRO_VERSION=true
- Lite版: flutter build apk --release --dart-define=IS_PRO_VERSION=false
次ステップ:
- lite-versionブランチ作成
- QR依存関係削除(pubspec.yaml)
- パッケージ名・アプリ名変更
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
21833e4e75
commit
f063ef6ec5
|
|
@ -49,7 +49,9 @@
|
||||||
"Bash(git checkout:*)",
|
"Bash(git checkout:*)",
|
||||||
"Bash(Select-Object -Last 10)",
|
"Bash(Select-Object -Last 10)",
|
||||||
"Bash(Select-Object -Last 30)",
|
"Bash(Select-Object -Last 30)",
|
||||||
"Bash(Select-String \"app_theme\")"
|
"Bash(Select-String \"app_theme\")",
|
||||||
|
"Bash(git tag:*)",
|
||||||
|
"Bash(Select-Object -Last 20)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,15 @@ import 'providers/theme_provider.dart';
|
||||||
import 'screens/main_screen.dart';
|
import 'screens/main_screen.dart';
|
||||||
import 'services/migration_service.dart';
|
import 'services/migration_service.dart';
|
||||||
|
|
||||||
|
/// Pro版かLite版かを判定するビルド時フラグ
|
||||||
|
///
|
||||||
|
/// ビルドコマンド:
|
||||||
|
/// - Pro版: flutter build apk --release --dart-define=IS_PRO_VERSION=true
|
||||||
|
/// - Lite版: flutter build apk --release --dart-define=IS_PRO_VERSION=false
|
||||||
|
///
|
||||||
|
/// デフォルトはtrue(Pro版)
|
||||||
|
const bool isProVersion = bool.fromEnvironment('IS_PRO_VERSION', defaultValue: true);
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -794,4 +794,4 @@ class _ExposureSliderPainter extends CustomPainter {
|
||||||
oldDelegate.minValue != minValue ||
|
oldDelegate.minValue != minValue ||
|
||||||
oldDelegate.maxValue != maxValue;
|
oldDelegate.maxValue != maxValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
import 'package:lucide_icons/lucide_icons.dart';
|
||||||
|
import '../main.dart'; // Import isProVersion flag
|
||||||
import '../providers/theme_provider.dart'; // Access userProfileProvider
|
import '../providers/theme_provider.dart'; // Access userProfileProvider
|
||||||
import '../providers/navigation_provider.dart'; // Track current tab index
|
import '../providers/navigation_provider.dart'; // Track current tab index
|
||||||
import '../utils/translations.dart'; // Translation helper
|
import '../utils/translations.dart'; // Translation helper
|
||||||
|
import '../widgets/pro_locked_screen.dart'; // Pro版ロック画面
|
||||||
import 'home_screen.dart';
|
import 'home_screen.dart';
|
||||||
import 'soul_screen.dart';
|
import 'soul_screen.dart';
|
||||||
import 'shop_settings_screen.dart';
|
import 'shop_settings_screen.dart';
|
||||||
|
|
@ -45,16 +47,44 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
||||||
final t = Translations(userProfile.locale); // Translation helper
|
final t = Translations(userProfile.locale); // Translation helper
|
||||||
|
|
||||||
// Define Screens for each mode
|
// Define Screens for each mode
|
||||||
|
// Pro版かLite版かで画面を切り替え
|
||||||
final List<Widget> screens = isBusiness
|
final List<Widget> screens = isBusiness
|
||||||
? [
|
? [
|
||||||
const HomeScreen(), // Inventory Management (FAB opens Menu Creation)
|
const HomeScreen(), // Inventory Management (FAB opens Menu Creation)
|
||||||
const InstaSupportScreen(), // Social Media Support
|
// Instagram Support: Pro版のみ
|
||||||
const AnalyticsScreen(), // Analytics
|
isProVersion
|
||||||
|
? const InstaSupportScreen()
|
||||||
|
: ProLockedScreen(
|
||||||
|
featureName: t['promo'],
|
||||||
|
featureIcon: LucideIcons.instagram,
|
||||||
|
description: userProfile.locale == 'ja'
|
||||||
|
? 'Instagram用の魅力的な投稿を自動生成。\n酒の写真とAI解析を活用して、\nプロモーションをサポートします。'
|
||||||
|
: 'Auto-generate attractive Instagram posts.\nLeverage sake photos and AI analysis\nto boost your promotion.',
|
||||||
|
),
|
||||||
|
// Analytics: Pro版のみ
|
||||||
|
isProVersion
|
||||||
|
? const AnalyticsScreen()
|
||||||
|
: ProLockedScreen(
|
||||||
|
featureName: t['analytics'],
|
||||||
|
featureIcon: LucideIcons.barChart,
|
||||||
|
description: userProfile.locale == 'ja'
|
||||||
|
? '在庫状況や人気銘柄を分析。\nビジネスの意思決定を\nデータで支援します。'
|
||||||
|
: 'Analyze inventory and popular brands.\nSupport business decisions\nwith data insights.',
|
||||||
|
),
|
||||||
const ShopSettingsScreen(), // Shop Settings
|
const ShopSettingsScreen(), // Shop Settings
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
const HomeScreen(), // My Sake List
|
const HomeScreen(), // My Sake List
|
||||||
const ScanARScreen(),
|
// QR Scan: Pro版のみ
|
||||||
|
isProVersion
|
||||||
|
? const ScanARScreen()
|
||||||
|
: ProLockedScreen(
|
||||||
|
featureName: t['scan'],
|
||||||
|
featureIcon: LucideIcons.scanLine,
|
||||||
|
description: userProfile.locale == 'ja'
|
||||||
|
? 'QRコードをスキャンして、\n酒の情報を素早く登録。\nAR表示で楽しく記録できます。'
|
||||||
|
: 'Scan QR codes to quickly register\nsake information.\nEnjoy recording with AR display.',
|
||||||
|
),
|
||||||
const SommelierScreen(),
|
const SommelierScreen(),
|
||||||
const BreweryMapScreen(),
|
const BreweryMapScreen(),
|
||||||
const SoulScreen(), // MyPage/Settings
|
const SoulScreen(), // MyPage/Settings
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:lucide_icons/lucide_icons.dart';
|
||||||
|
import '../providers/theme_provider.dart';
|
||||||
|
|
||||||
|
/// Pro版限定機能のロック画面
|
||||||
|
///
|
||||||
|
/// Lite版で使用制限されている機能を表示する際に使用。
|
||||||
|
/// 和モダンなデザインで、王冠アイコンと共に「Pro版限定」であることを伝える。
|
||||||
|
class ProLockedScreen extends ConsumerWidget {
|
||||||
|
final String featureName;
|
||||||
|
final IconData featureIcon;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
const ProLockedScreen({
|
||||||
|
super.key,
|
||||||
|
required this.featureName,
|
||||||
|
required this.featureIcon,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final userProfile = ref.watch(userProfileProvider);
|
||||||
|
final appColors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
// デフォルトの説明文を生成
|
||||||
|
final String displayDescription = description ?? _getDefaultDescription(featureName, userProfile.locale);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(featureName),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// 機能アイコン(グレーアウト)
|
||||||
|
Icon(
|
||||||
|
featureIcon,
|
||||||
|
size: 64,
|
||||||
|
color: appColors.onSurface.withValues(alpha: 0.3),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// 王冠アイコン(和モダンな細いライン)
|
||||||
|
Icon(
|
||||||
|
LucideIcons.crown,
|
||||||
|
size: 48,
|
||||||
|
color: appColors.primary.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Pro版限定メッセージ
|
||||||
|
Text(
|
||||||
|
userProfile.locale == 'ja'
|
||||||
|
? 'Pro版限定機能'
|
||||||
|
: 'Pro Version Only',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: appColors.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// 機能名
|
||||||
|
Text(
|
||||||
|
featureName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: appColors.primary,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// 説明文
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: appColors.primary.withValues(alpha: 0.05),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: Border.all(
|
||||||
|
color: appColors.primary.withValues(alpha: 0.2),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
displayDescription,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
height: 1.6,
|
||||||
|
color: appColors.onSurface.withValues(alpha: 0.8),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// Pro版へのアップグレード案内(将来実装用)
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Pro版購入画面への遷移(Phase 2で実装)
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
userProfile.locale == 'ja'
|
||||||
|
? 'Pro版は近日公開予定です'
|
||||||
|
: 'Pro version coming soon',
|
||||||
|
),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(LucideIcons.sparkles, size: 18),
|
||||||
|
label: Text(
|
||||||
|
userProfile.locale == 'ja'
|
||||||
|
? 'Pro版について'
|
||||||
|
: 'About Pro Version',
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: appColors.primary,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 機能名に応じたデフォルト説明文を生成
|
||||||
|
String _getDefaultDescription(String featureName, String locale) {
|
||||||
|
if (locale == 'ja') {
|
||||||
|
return 'この機能はPonshu Room Pro版でご利用いただけます。\n'
|
||||||
|
'Pro版では、より充実した酒ライフをサポートする\n'
|
||||||
|
'多彩な機能をお楽しみいただけます。';
|
||||||
|
} else {
|
||||||
|
return 'This feature is available in Ponshu Room Pro.\n'
|
||||||
|
'Enjoy enhanced sake experience with\n'
|
||||||
|
'a wide range of premium features.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue