import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/license_provider.dart'; import '../services/license_service.dart'; import '../theme/app_colors.dart'; import 'package:url_launcher/url_launcher.dart'; class LicenseScreen extends ConsumerStatefulWidget { const LicenseScreen({super.key}); @override ConsumerState createState() => _LicenseScreenState(); } class _LicenseScreenState extends ConsumerState { final _keyController = TextEditingController(); bool _isLoading = false; String? _errorMessage; @override void dispose() { _keyController.dispose(); super.dispose(); } Future _activate() async { final key = _keyController.text.trim(); if (key.isEmpty) return; setState(() { _isLoading = true; _errorMessage = null; }); final result = await LicenseService.activate(key); if (mounted) { if (result.success) { ref.invalidate(licenseStatusProvider); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Pro版ライセンスを有効化しました'), backgroundColor: Theme.of(context).extension()!.success, ), ); Navigator.of(context).pop(); } else { setState(() { _errorMessage = result.message; _isLoading = false; }); } } } void _openStore() async { const storeUrl = 'https://posimai-store.soar-enrich.com'; // TODO: 実際のストアURL final uri = Uri.parse(storeUrl); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } } @override Widget build(BuildContext context) { final colors = Theme.of(context).extension()!; final isPro = ref.watch(isProProvider); return Scaffold( backgroundColor: colors.brandSurface, appBar: AppBar( title: Text( 'ライセンスの有効化', style: TextStyle(color: colors.textPrimary, fontWeight: FontWeight.bold), ), backgroundColor: colors.brandSurface, elevation: 0, iconTheme: IconThemeData(color: colors.textPrimary), ), body: SafeArea( child: isPro ? _buildProState(colors) : _buildActivationForm(colors), ), ); } Widget _buildProState(AppColors colors) { return Center( child: Padding( padding: const EdgeInsets.all(32.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.verified, size: 64, color: colors.success), const SizedBox(height: 24), Text( 'Pro版ライセンス有効', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: colors.textPrimary, ), ), const SizedBox(height: 16), Text( 'すべてのPro機能をご利用いただけます。\nご購入ありがとうございました。', textAlign: TextAlign.center, style: TextStyle(color: colors.textSecondary, height: 1.5), ), ], ), ), ); } Widget _buildActivationForm(AppColors colors) { return SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Icon(Icons.key, size: 64, color: colors.brandPrimary), const SizedBox(height: 24), Text( 'ご購入ありがとうございます。', textAlign: TextAlign.center, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: colors.textPrimary, ), ), const SizedBox(height: 8), Text( 'メールで受け取った「PONSHU-」から始まる\nライセンスキーを入力してください。', textAlign: TextAlign.center, style: TextStyle(color: colors.textSecondary, height: 1.5), ), const SizedBox(height: 32), TextField( controller: _keyController, decoration: InputDecoration( hintText: 'PONSHU-XXXX-XXXX-XXXX', filled: true, fillColor: colors.surfaceSubtle, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colors.divider), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colors.divider), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: colors.brandPrimary, width: 2), ), prefixIcon: Icon(Icons.vpn_key_outlined, color: colors.iconSubtle), ), textCapitalization: TextCapitalization.characters, onSubmitted: (_) => _activate(), ), if (_errorMessage != null) ...[ const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: colors.error.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: colors.error.withValues(alpha: 0.3)), ), child: Row( children: [ Icon(Icons.error_outline, color: colors.error, size: 20), const SizedBox(width: 8), Expanded( child: Text( _errorMessage!, style: TextStyle(color: colors.error, fontSize: 13), ), ), ], ), ), ], const SizedBox(height: 24), ElevatedButton( onPressed: _isLoading ? null : _activate, style: ElevatedButton.styleFrom( backgroundColor: colors.brandPrimary, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) : const Text( '有効化する', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), const SizedBox(height: 48), Divider(color: colors.divider), const SizedBox(height: 24), Text( 'ライセンスをお持ちでない方', textAlign: TextAlign.center, style: TextStyle(color: colors.textSecondary, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), OutlinedButton( onPressed: _openStore, style: OutlinedButton.styleFrom( foregroundColor: colors.brandPrimary, side: BorderSide(color: colors.brandPrimary), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.shopping_cart_outlined, size: 20), SizedBox(width: 8), Text('ストアで購入する', style: TextStyle(fontWeight: FontWeight.bold)), ], ), ), ], ), ); } }