import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import '../../core/adjust/adjust_events.dart'; import '../../core/api/services/payment_api.dart'; import '../../core/auth/auth_service.dart'; import '../../core/theme/app_colors.dart'; import '../../core/user/user_state.dart'; import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_typography.dart'; import '../../shared/widgets/top_nav_bar.dart'; import 'models/activity_item.dart'; /// Recharge screen - matches Pencil tPjdN /// Tier cards (DC6NS, YRaOG, QlNz6) 对接 getGooglePayActivities / getApplePayActivities class RechargeScreen extends StatefulWidget { const RechargeScreen({super.key}); @override State createState() => _RechargeScreenState(); } class _RechargeScreenState extends State { List _activities = []; bool _loadingTiers = true; String? _tierError; @override void initState() { super.initState(); _fetchActivities(); } Future _fetchActivities() async { setState(() { _loadingTiers = true; _tierError = null; }); try { await AuthService.loginComplete; final country = UserState.navigate.value ?? ''; final res = defaultTargetPlatform == TargetPlatform.iOS ? await PaymentApi.getApplePayActivities( vambrace: country.isEmpty ? null : country, ) : await PaymentApi.getGooglePayActivities( vambrace: country.isEmpty ? null : country, ); if (!mounted) return; if (res.isSuccess && res.data != null) { final data = res.data as Map?; final summon = data?['summon'] as List? ?? []; final list = summon .whereType>() .map((e) => ActivityItem.fromJson(e)) .where((e) => e.code.isNotEmpty) .toList(); setState(() { _activities = list; _loadingTiers = false; }); } else { setState(() { _activities = []; _loadingTiers = false; _tierError = res.msg.isNotEmpty ? res.msg : 'Failed to load'; }); } } catch (e) { if (mounted) { setState(() { _activities = []; _loadingTiers = false; _tierError = e.toString(); }); } } } void _onBuy(ActivityItem item) { final price = AdjustEvents.parsePrice(item.actualAmount); if (price != null) { final tierToken = AdjustEvents.tierTokenFromPrice(price); if (tierToken != null) { AdjustEvents.trackTier(tierToken); } } final titleLower = item.title.toLowerCase(); if (titleLower.contains('monthly vip')) { AdjustEvents.trackMonthlyVip(); } else if (titleLower.contains('weekly vip')) { AdjustEvents.trackWeeklyVip(); } // TODO: 创建支付订单;成功时调用 AdjustEvents.trackPurchaseSuccess(),失败时调用 AdjustEvents.trackPaymentFailed() } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, appBar: PreferredSize( preferredSize: const Size.fromHeight(56), child: TopNavBar( title: 'Recharge', credits: UserCreditsData.of(context)?.creditsDisplay ?? '--', showBackButton: true, onBack: () => Navigator.of(context).pop(), onCreditsTap: null, ), ), body: SingleChildScrollView( padding: const EdgeInsets.only(bottom: AppSpacing.screenPaddingLarge), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _CreditsSection( currentCredits: UserCreditsData.of(context)?.creditsDisplay ?? '--', ), Padding( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.screenPaddingLarge, vertical: AppSpacing.xxl, ), child: Text( 'Select Tier', style: AppTypography.bodyMedium.copyWith( color: AppColors.textPrimary, ), ), ), if (_loadingTiers) const Padding( padding: EdgeInsets.all(AppSpacing.xxl), child: Center( child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2), ), ), ) else if (_tierError != null) Padding( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.screenPadding), child: Column( children: [ Text( _tierError!, style: AppTypography.bodyRegular.copyWith( color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.md), TextButton( onPressed: _fetchActivities, child: const Text('Retry'), ), ], ), ) else if (_activities.isEmpty) Padding( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.screenPadding), child: Text( 'No packages available', style: AppTypography.bodyRegular.copyWith( color: AppColors.textSecondary, ), ), ) else ...List.generate(_activities.length, (i) { final item = _activities[i]; final isRecommended = i == 1; final isPopular = i == 2; return Padding( padding: EdgeInsets.only( bottom: i < _activities.length - 1 ? AppSpacing.xl : 0, ), child: _TierCardFromActivity( item: item, badge: isRecommended ? _TierBadge.recommended : isPopular ? _TierBadge.popular : _TierBadge.none, onBuy: () => _onBuy(item), ), ); }), ], ), ), ); } } enum _TierBadge { none, recommended, popular } class _TierCardFromActivity extends StatelessWidget { const _TierCardFromActivity({ required this.item, required this.badge, required this.onBuy, }); final ActivityItem item; final _TierBadge badge; final VoidCallback onBuy; @override Widget build(BuildContext context) { final content = Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.screenPadding), padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xxl, vertical: AppSpacing.xl, ), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: AppColors.shadowLight, blurRadius: 6, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.creditsDisplay, style: AppTypography.bodyLarge.copyWith( color: AppColors.textPrimary, ), ), SizedBox(height: badge == _TierBadge.none ? AppSpacing.xs : AppSpacing.sm), Text( item.priceDisplay, style: AppTypography.bodyRegular.copyWith( color: AppColors.textSecondary, ), ), ], ), _BuyButton(onTap: onBuy), ], ), ); if (badge == _TierBadge.none) { return content; } return Stack( clipBehavior: Clip.none, children: [ content, Positioned( top: 0, right: AppSpacing.screenPadding, child: Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.xs, ), decoration: BoxDecoration( color: badge == _TierBadge.recommended ? AppColors.primaryLight : AppColors.accentOrangeLight, borderRadius: const BorderRadius.only( topRight: Radius.circular(16), bottomLeft: Radius.circular(16), ), ), child: Text( badge == _TierBadge.recommended ? 'Recommended' : 'Most Popular', style: AppTypography.label.copyWith( color: badge == _TierBadge.recommended ? AppColors.primary : AppColors.accentOrange, fontWeight: FontWeight.w600, ), ), ), ), ], ); } } class _CreditsSection extends StatelessWidget { const _CreditsSection({required this.currentCredits}); final String currentCredits; @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.all(AppSpacing.screenPadding), padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xxl, vertical: AppSpacing.xl, ), decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.primary.withValues(alpha: 0.5), ), boxShadow: [ BoxShadow( color: AppColors.primaryShadow.withValues(alpha: 0.25), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Icon(LucideIcons.sparkles, size: 28, color: AppColors.surface), const SizedBox(width: AppSpacing.md), Text( currentCredits, style: AppTypography.bodyLarge.copyWith( fontSize: 32, fontWeight: FontWeight.w700, color: AppColors.surface, ), ), const Spacer(), Text( 'Current Credits', style: AppTypography.caption.copyWith( color: AppColors.surface.withValues(alpha: 0.8), ), ), ], ), ); } } class _BuyButton extends StatelessWidget { const _BuyButton({required this.onTap}); final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xl, vertical: AppSpacing.md, ), decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(12), ), child: Text( 'Buy', style: AppTypography.bodyRegular.copyWith( color: AppColors.surface, fontWeight: FontWeight.w600, ), ), ), ); } }