import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:provider/provider.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/app/constants/sc_screen.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/app_localizations.dart'; import 'package:yumi/modules/wallet/wallet_route.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/services/payment/apple_payment_manager.dart'; import 'package:yumi/services/payment/google_payment_manager.dart'; import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; import 'package:yumi/ui_kit/components/text/sc_text.dart'; class RechargePage extends StatefulWidget { const RechargePage({super.key}); @override State createState() => _RechargePageState(); } class _RechargePageState extends State { static const Color _accentGoldColor = Color(0xffF5C550); static const Color _surfaceTextColor = Colors.white; static const Color _surfaceSubtleTextColor = Color(0xCCFFFFFF); @override void initState() { super.initState(); Provider.of(context, listen: false).balance(); if (Platform.isAndroid) { Provider.of( context, listen: false, ).initializePaymentProcessor(context); } else if (Platform.isIOS) { Provider.of( context, listen: false, ).initializePaymentProcessor(context); } } @override Widget build(BuildContext context) { return Stack( children: [ Image.asset( SCGlobalConfig.businessLogicStrategy.getRechargePageBackgroundImage(), width: ScreenUtil().screenWidth, fit: BoxFit.fill, ), Scaffold( backgroundColor: SCGlobalConfig.businessLogicStrategy .getRechargePageScaffoldBackgroundColor(), resizeToAvoidBottomInset: false, appBar: SocialChatStandardAppBar( title: SCAppLocalizations.of(context)!.recharge, actions: [ GestureDetector( onTap: _showRecordDialog, child: Container( margin: EdgeInsetsDirectional.only(end: 15.w), child: Image.asset( SCGlobalConfig.businessLogicStrategy .getRechargePageRecordIcon(), width: 24.w, height: 24.w, ), ), ), ], ), body: SafeArea( top: false, child: Column( children: [ _buildWalletSection(), SizedBox(height: 36.w), Expanded( child: Padding( padding: EdgeInsets.fromLTRB(12.w, 8.w, 12.w, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildMethodSection(), SizedBox(height: 14.w), Expanded(child: _buildNativeProductList()), if (Platform.isIOS) ...[ SizedBox(height: 12.w), _buildFooterButton( title: SCAppLocalizations.of( context, )!.restorePurchases, onTap: () { Provider.of( context, listen: false, ).recoverTransactions(); }, ), ], SizedBox(height: 12.w), _buildFooterButton( title: SCAppLocalizations.of(context)!.recharge, onTap: _processNativePurchase, ), SizedBox(height: 14.w), ], ), ), ), ], ), ), ), ], ); } void _showRecordDialog() { showGeneralDialog( context: context, barrierLabel: '', barrierDismissible: true, transitionDuration: const Duration(milliseconds: 350), barrierColor: SCGlobalConfig.businessLogicStrategy .getRechargePageDialogBarrierColor(), pageBuilder: ( BuildContext context, Animation animation, Animation secondaryAnimation, ) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.pop(context); setState(() {}); }, child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ SizedBox(height: height(68)), Row( children: [ const Spacer(), GestureDetector( child: Container( decoration: BoxDecoration( color: SCGlobalConfig.businessLogicStrategy .getRechargePageDialogContainerBackgroundColor(), borderRadius: BorderRadius.circular(4), ), padding: EdgeInsets.symmetric( horizontal: 8.w, vertical: 3.w, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 5.w), GestureDetector( child: text( SCAppLocalizations.of(context)!.goldListort, textColor: SCGlobalConfig.businessLogicStrategy .getRechargePageDialogTextColor(), fontSize: 14.sp, ), onTap: () { Navigator.of(context).pop(); SCNavigatorUtils.push( context, WalletRoute.goldRecord, replace: false, ); }, ), SizedBox(height: 5.w), ], ), ), onTap: () {}, ), SizedBox(width: 10.w), ], ), Expanded(child: SizedBox(width: width(1))), ], ), ); }, ); } Widget _buildWalletSection() { return Container( margin: EdgeInsets.symmetric(horizontal: 15.w), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 45.w), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( SCGlobalConfig.businessLogicStrategy.getRechargePageGoldIcon(), width: 36.w, height: 36.w, ), SizedBox(width: 3.w), Consumer( builder: (context, ref, child) { return text( ref.myBalance > 1000 ? "${(ref.myBalance / 1000).toStringAsFixed(2)}k" : "${ref.myBalance}", fontSize: 16.sp, textColor: SCGlobalConfig.businessLogicStrategy .getRechargePageWalletTextColor(), fontWeight: FontWeight.bold, ); }, ), ], ), ], ), ); } Widget _buildMethodSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Recharge methods', style: TextStyle( fontSize: 15.sp, color: _accentGoldColor, fontWeight: FontWeight.w700, ), ), SizedBox(height: 10.w), Container( width: double.infinity, height: 58.w, padding: EdgeInsets.symmetric(horizontal: 14.w), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0x42FFFFFF), Color(0x24FFFFFF)], begin: AlignmentDirectional.centerStart, end: AlignmentDirectional.centerEnd, ), borderRadius: BorderRadius.circular(10.w), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 10.w, offset: Offset(0, 3.w), ), ], ), child: Row( children: [ _buildMethodLeading(), SizedBox(width: 14.w), Expanded( child: Text( Platform.isAndroid ? 'Google Pay' : 'Apple Pay', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15.sp, color: _surfaceTextColor, fontWeight: FontWeight.w600, ), ), ), Icon( Icons.radio_button_checked, size: 18.w, color: _accentGoldColor, ), ], ), ), ], ); } Widget _buildMethodLeading() { if (Platform.isAndroid) { return Image.asset( 'sc_images/login/sc_icon_google.png', width: 28.w, height: 28.w, fit: BoxFit.contain, ); } return Icon(Icons.apple, size: 24.w, color: _surfaceTextColor); } Widget _buildNativeProductList() { return Platform.isAndroid ? Consumer( builder: (context, ref, child) { return ref.products.isNotEmpty ? ListView.separated( itemCount: ref.products.length, padding: EdgeInsets.zero, itemBuilder: (context, index) { return _buildGoogleProductItem( ref.products[index], ref, index, ); }, separatorBuilder: (BuildContext context, int index) { return SizedBox(height: 10.w); }, ) : _buildRechargeEmptyState(); }, ) : Consumer( builder: (context, ref, child) { return ref.products.isNotEmpty ? ListView.separated( itemCount: ref.products.length, padding: EdgeInsets.zero, itemBuilder: (context, index) { return _buildAppleProductItem( ref.products[index], ref, index, ); }, separatorBuilder: (BuildContext context, int index) { return SizedBox(height: 10.w); }, ) : _buildRechargeEmptyState(); }, ); } Widget _buildRechargeEmptyState() { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Image.asset( 'sc_images/general/sc_icon_loading.png', width: 96.w, fit: BoxFit.fitWidth, color: Colors.white.withValues(alpha: 0.72), ), SizedBox(height: 10.w), Text( SCAppLocalizations.of(context)!.noData, style: TextStyle( fontSize: 13.sp, color: _accentGoldColor.withValues(alpha: 0.9), fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildFooterButton({ required String title, required VoidCallback onTap, }) { return SCDebounceWidget( onTap: onTap, child: Container( alignment: Alignment.center, margin: EdgeInsets.symmetric(horizontal: 60.w), height: 42.w, decoration: BoxDecoration( color: SCGlobalConfig.businessLogicStrategy .getRechargePageButtonBackgroundColor(), borderRadius: BorderRadius.all(Radius.circular(height(8))), ), child: text( title, textColor: SCGlobalConfig.businessLogicStrategy .getRechargePageButtonTextColor(), fontSize: 16.sp, ), ), ); } void _processNativePurchase() { if (Platform.isAndroid) { Provider.of( context, listen: false, ).processPurchase(); } else if (Platform.isIOS) { Provider.of( context, listen: false, ).processPurchase(); } } Widget _buildGoogleProductItem( SelecteProductConfig productConfig, AndroidPaymentProcessor ref, int index, ) { return GestureDetector( child: Container( padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 10.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.w), gradient: LinearGradient( colors: productConfig.isSelecte ? const [Color(0x42FFFFFF), Color(0x24FFFFFF)] : const [Color(0x33FFFFFF), Color(0x18FFFFFF)], begin: AlignmentDirectional.centerStart, end: AlignmentDirectional.centerEnd, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 10.w, offset: Offset(0, 3.w), ), ], ), child: Row( children: [ Image.asset( SCGlobalConfig.businessLogicStrategy .getRechargePageGoldIconByIndex(index), width: 34.w, height: 34.w, ), SizedBox(width: 5.w), text( "${ref.productMap[productConfig.produc.id]?.obtainCandy ?? 0}", fontSize: 12.sp, textColor: _surfaceTextColor, ), const Spacer(), text( productConfig.produc.price, textColor: _surfaceSubtleTextColor, fontWeight: FontWeight.w600, fontSize: 12.sp, ), ], ), ), onTap: () { ref.chooseProductConfig(index); }, ); } Widget _buildAppleProductItem( SelecteProductConfig productConfig, IOSPaymentProcessor ref, int index, ) { return GestureDetector( child: Container( padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 10.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.w), gradient: LinearGradient( colors: productConfig.isSelecte ? const [Color(0x42FFFFFF), Color(0x24FFFFFF)] : const [Color(0x33FFFFFF), Color(0x18FFFFFF)], begin: AlignmentDirectional.centerStart, end: AlignmentDirectional.centerEnd, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 10.w, offset: Offset(0, 3.w), ), ], ), child: Row( children: [ Image.asset( SCGlobalConfig.businessLogicStrategy .getRechargePageGoldIconByIndex(index), width: 34.w, height: 34.w, ), SizedBox(width: 5.w), text( "${ref.productMap[productConfig.produc.id]?.obtainCandy ?? 0}", fontSize: 12.sp, textColor: _surfaceTextColor, ), const Spacer(), text( productConfig.produc.price, textColor: _surfaceSubtleTextColor, fontWeight: FontWeight.w600, fontSize: 12.sp, ), ], ), ), onTap: () { ref.chooseProductConfig(index); }, ); } } class SelecteProductConfig { ProductDetails produc; bool isSelecte = false; SelecteProductConfig(this.produc, this.isSelecte); }