import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'models.dart'; import 'paywall_view_model.dart'; /// 付费墙:拉取套餐价格 → 选套餐 → 微信/支付宝支付 → 解锁进入相机 class PaywallScreen extends StatefulWidget { const PaywallScreen({super.key}); @override State createState() => _PaywallScreenState(); } class _PaywallScreenState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().loadPlans(); }); } @override Widget build(BuildContext context) { final vm = context.watch(); final loading = vm.state.state == PayState.loading; return Scaffold( appBar: AppBar(title: const Text('视野 · 会员'), centerTitle: true), body: SafeArea( child: Column( children: [ const Padding( padding: EdgeInsets.fromLTRB(24, 16, 24, 16), child: Text( '开通会员解锁完整功能,按自然日计费,到期自动失效', style: TextStyle(color: Colors.grey, fontSize: 13), ), ), Expanded( child: ListView( padding: const EdgeInsets.symmetric(horizontal: 24), children: [ _PlansSection(vm: vm, loading: loading), const SizedBox(height: 24), _PayButton( label: '微信支付', icon: Icons.wechat, color: const Color(0xFF07C160), enabled: !loading && vm.state.selectedPlan != null, onTap: loading ? null : () async { final license = await vm.pay(PayChannel.wechat); if (license != null && context.mounted) { _onPaid(context); } }, ), const SizedBox(height: 12), _PayButton( label: '支付宝支付', icon: Icons.account_balance_wallet, color: const Color(0xFF1677FF), enabled: !loading && vm.state.selectedPlan != null, onTap: loading ? null : () async { final license = await vm.pay(PayChannel.alipay); if (license != null && context.mounted) { _onPaid(context); } }, ), const SizedBox(height: 12), _PayButton( label: '联系管理员', icon: Icons.headset_mic, color: Colors.grey.shade600, enabled: true, onTap: () async { await Clipboard.setData( const ClipboardData(text: 'wenwu901'), ); if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('已复制管理员微信号,请使用微信添加联系人'), ), ); }, ), if (vm.state.state == PayState.failed) Padding( padding: const EdgeInsets.only(top: 16), child: Text( vm.state.message ?? '支付失败', textAlign: TextAlign.center, style: const TextStyle(color: Colors.red), ), ), const SizedBox(height: 24), ], ), ), ], ), ), ); } /// 支付成功:返回主界面(主界面刷新展示新到期时间) void _onPaid(BuildContext context) { Navigator.of(context).pop(); } } /// 套餐区三态:加载中 / 失败可重试 / 套餐卡片列表 class _PlansSection extends StatelessWidget { final PaywallViewModel vm; final bool loading; const _PlansSection({required this.vm, required this.loading}); @override Widget build(BuildContext context) { if (vm.plansLoading && vm.plans.isEmpty) { return const Padding( padding: EdgeInsets.symmetric(vertical: 48), child: Center(child: CircularProgressIndicator()), ); } if (vm.plansError != null && vm.plans.isEmpty) { return Padding( padding: const EdgeInsets.symmetric(vertical: 32), child: Column( children: [ Text( vm.plansError!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.red), ), const SizedBox(height: 12), OutlinedButton( onPressed: () => context.read().loadPlans(), child: const Text('重试'), ), ], ), ); } return Column( children: [ ...vm.plans.map((p) => _PlanCard( plan: p, selected: vm.state.selectedPlan?.id == p.id, enabled: !loading, onTap: () => vm.selectPlan(p), )), ], ); } } class _PlanCard extends StatelessWidget { final Plan plan; final bool selected; final bool enabled; final VoidCallback onTap; const _PlanCard({ required this.plan, required this.selected, required this.enabled, required this.onTap, }); @override Widget build(BuildContext context) { return Card( elevation: selected ? 3 : 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: selected ? Colors.orange : Colors.grey.shade300, width: selected ? 2 : 1, ), ), child: ListTile( onTap: enabled ? onTap : null, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), title: Text( plan.label, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 17), ), subtitle: Text('${plan.days} 天 · 自然日'), trailing: Text( '¥${plan.priceYuan}', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), ); } } class _PayButton extends StatelessWidget { final String label; final IconData icon; final Color color; final bool enabled; final VoidCallback? onTap; const _PayButton({ required this.label, required this.icon, required this.color, required this.enabled, required this.onTap, }); @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, height: 48, child: FilledButton.icon( style: FilledButton.styleFrom( backgroundColor: color, disabledBackgroundColor: Colors.grey.shade300, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), ), onPressed: enabled ? onTap : null, icon: Icon(icon, size: 20), label: Text(label, style: const TextStyle(fontSize: 16)), ), ); } }