import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/subscription_model.dart'; import '../../services/subscription_service.dart'; import 'subscription_details_screen.dart'; import '../../widgets/dialog_actions.dart'; class SubscriptionScreen extends StatefulWidget { const SubscriptionScreen({super.key}); @override State createState() => _SubscriptionScreenState(); } class _SubscriptionScreenState extends State { bool _isYearly = true; // 기본값은 연간 구독 @override void initState() { super.initState(); // 화면 로드 시 구독 정보 조회 WidgetsBinding.instance.addPostFrameCallback((_) { final subscriptionService = Provider.of( context, listen: false, ); subscriptionService.loadCurrentSubscription(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('구독 관리')), body: Consumer( builder: (context, subscriptionService, child) { if (subscriptionService.isLoading) { return const Center(child: CircularProgressIndicator()); } if (subscriptionService.error != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('오류가 발생했습니다', style: TextStyle(color: Colors.red[700])), const SizedBox(height: 8), Text(subscriptionService.error!), const SizedBox(height: 16), ElevatedButton( onPressed: () => subscriptionService.loadCurrentSubscription(), child: const Text('다시 시도'), ), ], ), ); } // 현재 구독 정보가 있는 경우 if (subscriptionService.hasSubscription) { return _buildCurrentSubscription(subscriptionService); } // 구독 정보가 없는 경우 플랜 선택 화면 return _buildSubscriptionPlans(subscriptionService); }, ), ); } Widget _buildCurrentSubscription(SubscriptionService subscriptionService) { final subscription = subscriptionService.currentSubscription!; final plan = subscriptionService.availablePlans.firstWhere( (plan) => plan.type == subscription.planType, orElse: () => SubscriptionPlan.getDefaultPlans().first, ); return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 현재 구독 정보 카드 Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide( color: subscription.isActive ? Colors.green : Colors.grey, width: 2, ), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.verified, color: subscription.isActive ? Colors.green : Colors.grey, size: 28, ), const SizedBox(width: 8), Text( '${plan.name} 플랜', style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, ), ), const Spacer(), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: subscription.isActive ? Colors.green.withOpacity(0.1) : Colors.grey.withOpacity(0.1), borderRadius: BorderRadius.circular(16), border: Border.all( color: subscription.isActive ? Colors.green : Colors.grey, ), ), child: Text( subscription.isActive ? '활성' : '만료됨', style: TextStyle( color: subscription.isActive ? Colors.green : Colors.grey, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 16), const Divider(), const SizedBox(height: 16), _buildInfoRow('시작일', _formatDate(subscription.startDate)), const SizedBox(height: 8), _buildInfoRow('만료일', _formatDate(subscription.endDate)), const SizedBox(height: 8), _buildInfoRow( '자동 갱신', subscription.autoRenew ? '활성화' : '비활성화', ), const SizedBox(height: 8), _buildInfoRow('남은 기간', '${subscription.daysLeft}일'), const SizedBox(height: 16), const Divider(), const SizedBox(height: 16), const Text( '플랜 혜택', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ...plan.features.map( (feature) => Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon( Icons.check_circle, color: Colors.green, size: 16, ), const SizedBox(width: 8), Text(feature), ], ), ), ), ], ), ), ), const SizedBox(height: 24), // 구독 관리 버튼 Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: subscription.isActive ? () => _showCancelDialog(subscriptionService) : null, icon: const Icon(Icons.cancel), label: const Text('구독 취소'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade100, foregroundColor: Colors.red.shade700, ), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton.icon( onPressed: () => _toggleAutoRenew(subscriptionService), icon: Icon( subscription.autoRenew ? Icons.toggle_on : Icons.toggle_off, ), label: Text(subscription.autoRenew ? '자동 갱신 끄기' : '자동 갱신 켜기'), ), ), ], ), const SizedBox(height: 16), // 플랜 변경 버튼 ElevatedButton.icon( onPressed: () { setState(() { // 플랜 선택 화면으로 전환 subscriptionService.loadCurrentSubscription(); }); }, icon: const Icon(Icons.upgrade), label: const Text('플랜 변경하기'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, minimumSize: const Size(double.infinity, 50), ), ), ], ), ); } Widget _buildSubscriptionPlans(SubscriptionService subscriptionService) { final plans = subscriptionService.availablePlans; return Column( children: [ // 연간/월간 선택 토글 Padding( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('월간 구독'), const SizedBox(width: 8), Switch( value: _isYearly, onChanged: (value) { setState(() { _isYearly = value; }); }, activeThumbColor: Colors.blue, ), const SizedBox(width: 8), Row( children: [ const Text('연간 구독'), Container( margin: const EdgeInsets.only(left: 8), padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: Colors.green.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.green), ), child: const Text( '20% 할인', style: TextStyle( color: Colors.green, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ), // 플랜 목록 Expanded( child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: plans.length, itemBuilder: (context, index) { final plan = plans[index]; final price = _isYearly ? plan.yearlyPrice : plan.monthlyPrice; final period = _isYearly ? '년' : '월'; return Card( margin: const EdgeInsets.only(bottom: 16), elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: InkWell( onTap: () { Navigator.of(context).push( MaterialPageRoute( builder: (context) => SubscriptionDetailsScreen( plan: plan, isYearly: _isYearly, ), ), ); }, borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( plan.name, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), const Spacer(), if (plan.type == SubscriptionPlanType.premium) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: Colors.amber.withOpacity(0.1), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.amber), ), child: const Text( '인기', style: TextStyle( color: Colors.amber, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 8), Text(plan.description), const SizedBox(height: 16), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '₩${_formatPrice(price)}', style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 4), Text('/$period'), const Spacer(), const Icon(Icons.arrow_forward), ], ), const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), ...plan.features .take(3) .map( (feature) => Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( children: [ const Icon( Icons.check_circle, color: Colors.green, size: 16, ), const SizedBox(width: 8), Text(feature), ], ), ), ), if (plan.features.length > 3) Text( '외 ${plan.features.length - 3}개 혜택', style: TextStyle( color: Colors.grey[600], fontStyle: FontStyle.italic, ), ), ], ), ), ), ); }, ), ), ], ); } Widget _buildInfoRow(String label, String value) { return Row( children: [ Text('$label: ', style: const TextStyle(fontWeight: FontWeight.bold)), Text(value), ], ); } String _formatDate(DateTime date) { return '${date.year}년 ${date.month}월 ${date.day}일'; } String _formatPrice(double price) { if (price == 0) { return '0'; } // 천 단위 콤마 추가 final priceInt = price.toInt(); return priceInt.toString().replaceAllMapped( RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},', ); } void _showCancelDialog(SubscriptionService subscriptionService) { final messenger = ScaffoldMessenger.of(context); final navigator = Navigator.of(context); showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('구독 취소'), content: const Text( '정말로 구독을 취소하시겠습니까?\n' '취소하면 현재 구독 기간이 끝날 때까지만 서비스를 이용할 수 있습니다.', ), actions: DialogActions.confirm( onCancel: () => Navigator.of(dialogContext).pop(), onConfirm: () async { navigator.pop(); final success = await subscriptionService.cancelSubscription(); if (success && mounted) { messenger.showSnackBar( const SnackBar(content: Text('구독이 취소되었습니다')), ); } }, destructive: true, confirmText: '예, 취소합니다', ), ), ); } void _toggleAutoRenew(SubscriptionService subscriptionService) async { final messenger = ScaffoldMessenger.of(context); final success = await subscriptionService.toggleAutoRenew(); if (success && mounted) { final isAutoRenew = subscriptionService.currentSubscription!.autoRenew; messenger.showSnackBar( SnackBar( content: Text(isAutoRenew ? '자동 갱신이 활성화되었습니다' : '자동 갱신이 비활성화되었습니다'), ), ); } } }