import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:provider/provider.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../../../core/theme/app_color_scheme.dart'; import '../../../core/theme/app_spacing.dart'; import '../../../core/utils/toast_utils.dart'; import '../../../core/event/app_event_bus.dart'; import '../../../data/models/account_models.dart'; import '../../../providers/asset_provider.dart'; import '../../../providers/auth_provider.dart'; import '../../shared/ui_constants.dart'; import '../../components/glass_panel.dart'; import '../../components/neon_glow.dart'; import '../orders/fund_orders_page.dart'; import 'transfer_page.dart'; /// 资产页面 - Matching .pen design spec (CMcqs) class AssetPage extends StatefulWidget { const AssetPage({super.key}); @override State createState() => _AssetPageState(); } class _AssetPageState extends State with AutomaticKeepAliveClientMixin { int _activeTab = 0; StreamSubscription? _eventSub; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _loadData(); _listenEvents(); }); } @override void dispose() { _eventSub?.cancel(); super.dispose(); } void _listenEvents() { final eventBus = context.read(); _eventSub = eventBus.on(AppEventType.assetChanged, (_) { if (mounted) { context.read().refreshAll(force: true); } }); } void _loadData() { context.read().refreshAll(force: true); } @override Widget build(BuildContext context) { super.build(context); final colorScheme = Theme.of(context).colorScheme; return Scaffold( backgroundColor: colorScheme.background, body: Consumer( builder: (context, provider, _) { return RefreshIndicator( onRefresh: () => provider.refreshAll(force: true), color: colorScheme.primary, backgroundColor: colorScheme.surfaceContainerHighest, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(AppSpacing.md, AppSpacing.md + 8, AppSpacing.md, 32), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Page title: "资产" 22px bold — matching .pen titleFrame padding [16,0,8,0] Padding( padding: const EdgeInsets.only(top: 16, bottom: 8), child: Text( '资产', style: GoogleFonts.inter( fontSize: 22, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), ), const SizedBox(height: AppSpacing.sm), // Account tab switcher — pill-style matching .pen UE6xC _AccountTabSwitcher( selectedIndex: _activeTab, onChanged: (index) => setState(() => _activeTab = index), ), const SizedBox(height: AppSpacing.md), // Balance card — matching .pen 59637 (cornerRadius lg, stroke, padding 20, gap 12) _BalanceCard( provider: provider, activeTab: _activeTab, ), const SizedBox(height: AppSpacing.md), // Action buttons row — matching .pen pIpHe (gap 12) _ActionButtonsRow( onDeposit: () => _showDepositDialog(context), onWithdraw: () => _showWithdrawDialog(context, provider.fundAccount?.balance), onTransfer: () => _navigateToTransfer(context), ), const SizedBox(height: AppSpacing.md), // Records link row — matching .pen fLHtq (cornerRadius lg, padding [14,16], stroke) _RecordsLinkRow( onTap: () => Navigator.push( context, MaterialPageRoute(builder: (_) => const FundOrdersPage()), ), ), const SizedBox(height: AppSpacing.md), // Holdings section — matching .pen th9BG + 6X6tC _HoldingsSection(holdings: _activeTab == 1 ? provider.holdings : []), ], ), ), ); }, ), ); } } // ============================================ // Account Tab Switcher — .pen node UE6xC // height: 40, padding: 3, cornerRadius: md, fill: $bg-tertiary // activeTab: fill $bg-primary, cornerRadius sm, shadow blur 3, color #0000000D, offset y 1 // activeTabText: 14px, fontWeight 600, fill $text-primary // inactiveTabText: 14px, fontWeight 500, fill $text-secondary // ============================================ class _AccountTabSwitcher extends StatelessWidget { final int selectedIndex; final ValueChanged onChanged; const _AccountTabSwitcher({ required this.selectedIndex, required this.onChanged, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; return Container( height: 40, padding: const EdgeInsets.all(3), decoration: BoxDecoration( color: isDark ? colorScheme.surfaceContainerHighest : colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(AppRadius.md), ), child: Row( children: [ _buildTab( context: context, label: '资金账户', isSelected: selectedIndex == 0, onTap: () => onChanged(0), isDark: isDark, ), _buildTab( context: context, label: '交易账户', isSelected: selectedIndex == 1, onTap: () => onChanged(1), isDark: isDark, ), ], ), ); } Widget _buildTab({ required BuildContext context, required String label, required bool isSelected, required VoidCallback onTap, required bool isDark, }) { final colorScheme = Theme.of(context).colorScheme; return Expanded( child: GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), decoration: BoxDecoration( color: isSelected ? colorScheme.surface : Colors.transparent, borderRadius: BorderRadius.circular(AppRadius.sm), boxShadow: isSelected ? [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 3, offset: const Offset(0, 1), ), ] : null, ), alignment: Alignment.center, child: Text( label, style: GoogleFonts.inter( fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, color: isSelected ? colorScheme.onSurface : colorScheme.onSurfaceVariant, ), ), ), ), ); } } // ============================================ // Balance Card — .pen node 59637 // cornerRadius: lg, fill: $surface-card, padding: 20, stroke: $border-default 1px, gap: 12 // balLabel: "USDT 余额" 12px normal $text-secondary // balAmount: "25,680.50" 28px w700 $text-primary // balSubRow: "≈ $25,680.50 USD" 12px normal $text-muted // ============================================ class _BalanceCard extends StatelessWidget { final AssetProvider provider; final int activeTab; const _BalanceCard({ required this.provider, required this.activeTab, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final displayBalance = activeTab == 0 ? (provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00') : _calculateTradeTotal(); return GlassPanel( padding: const EdgeInsets.all(20), borderRadius: BorderRadius.circular(AppRadius.lg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'USDT 余额', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.normal, color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 12), Text( _formatBalance(displayBalance), style: GoogleFonts.inter( fontSize: 28, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), const SizedBox(height: 12), Text( '\u2248 \$${_formatBalance(displayBalance)} USD', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.normal, color: isDark ? AppColorScheme.darkOnSurfaceMuted : colorScheme.onSurfaceVariant, ), ), ], ), ); } String _calculateTradeTotal() { double total = 0; for (var h in provider.holdings) { total += double.tryParse(h.currentValue?.toString() ?? '0') ?? 0; } return total.toStringAsFixed(2); } String _formatBalance(String balance) { final d = double.tryParse(balance) ?? 0; return d.toStringAsFixed(2).replaceAllMapped( RegExp(r'\B(?=(\d{3})+(?!\d))'), (Match m) => ',', ); } } // ============================================ // Action Buttons Row — .pen node pIpHe // gap: 12, three buttons evenly distributed // Each button: circle 48x48 fill $bg-tertiary, cornerRadius 24 // icon: 20px $accent-primary (lucide: arrow-up-right / arrow-down-left / repeat) // label: 12px w500 $text-secondary // ============================================ class _ActionButtonsRow extends StatelessWidget { final VoidCallback onDeposit; final VoidCallback onWithdraw; final VoidCallback onTransfer; const _ActionButtonsRow({ required this.onDeposit, required this.onWithdraw, required this.onTransfer, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final accentColor = isDark ? colorScheme.secondary : colorScheme.primary; final bgColor = isDark ? colorScheme.surfaceContainerHighest : colorScheme.surfaceContainerHigh; return Row( children: [ _ActionButton( icon: LucideIcons.arrowUpRight, label: '充值', accentColor: accentColor, bgColor: bgColor, onTap: onDeposit, ), const SizedBox(width: 12), _ActionButton( icon: LucideIcons.arrowDownLeft, label: '提现', accentColor: accentColor, bgColor: bgColor, onTap: onWithdraw, ), const SizedBox(width: 12), _ActionButton( icon: LucideIcons.repeat, label: '划转', accentColor: accentColor, bgColor: bgColor, onTap: onTransfer, ), ], ); } } /// Single action button — matching .pen btn1/btn2/btn3 class _ActionButton extends StatelessWidget { final IconData icon; final String label; final Color accentColor; final Color bgColor; final VoidCallback onTap; const _ActionButton({ required this.icon, required this.label, required this.accentColor, required this.bgColor, required this.onTap, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Expanded( child: GestureDetector( onTap: onTap, child: Column( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: bgColor, shape: BoxShape.circle, ), alignment: Alignment.center, child: Icon( icon, size: 20, color: accentColor, ), ), const SizedBox(height: 6), Text( label, style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, color: colorScheme.onSurfaceVariant, ), ), ], ), ), ); } } // ============================================ // Records Link Row — .pen node fLHtq // cornerRadius: lg, fill: $surface-card, padding: [14, 16], stroke: $border-default 1px // recordsText: "充提记录" 14px w500 $text-primary // recordsChevron: lucide chevron-right 16px $text-muted // ============================================ class _RecordsLinkRow extends StatelessWidget { final VoidCallback onTap; const _RecordsLinkRow({required this.onTap}); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final mutedColor = isDark ? AppColorScheme.darkOnSurfaceMuted : colorScheme.onSurfaceVariant; return GestureDetector( onTap: onTap, child: GlassPanel( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: 14), borderRadius: BorderRadius.circular(AppRadius.lg), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '充提记录', style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.w500, color: colorScheme.onSurface, ), ), Icon( LucideIcons.chevronRight, size: 16, color: mutedColor, ), ], ), ), ); } } // ============================================ // Holdings Section — .pen nodes th9BG (header) + 6X6tC (card) // Holdings Header: "交易账户持仓" 16px w600 $text-primary | "查看全部 >" 12px normal $text-secondary // Holdings Card: cornerRadius lg, fill $surface-card, stroke $border-default 1px // Each row: padding [14, 16], justifyContent space_between // Left: avatar (36x36, cornerRadius 18, fill $accent-light) + coin info (gap 2) // AvatarText: coinCode first letter, 14px w700 $accent-primary // CoinName: 14px w600 $text-primary // CoinAmt: 12px normal $text-secondary // Right: value + pnl (gap 2, alignItems end) // Value: 13px w500 $text-primary // Pnl: 12px w500, $profit-green / $loss-red // Divider: $border-default height 1 opacity 0.5 // ============================================ class _HoldingsSection extends StatelessWidget { final List holdings; const _HoldingsSection({required this.holdings}); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Column( children: [ // Header row: "交易账户持仓" + "查看全部 >" Padding( padding: const EdgeInsets.only(bottom: AppSpacing.sm), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '交易账户持仓', style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w600, color: colorScheme.onSurface, ), ), Text( '查看全部 >', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.normal, color: colorScheme.onSurfaceVariant, ), ), ], ), ), // Holdings card — uses real provider.holdings data if (holdings.isEmpty) Padding( padding: const EdgeInsets.all(AppSpacing.xl), child: Text( '暂无持仓', style: GoogleFonts.inter( fontSize: 13, color: colorScheme.onSurfaceVariant, ), ), ) else GlassPanel( padding: EdgeInsets.zero, borderRadius: BorderRadius.circular(AppRadius.lg), child: Column( children: List.generate(holdings.length, (index) { final h = holdings[index] as AccountTrade; final isProfit = h.profitRate >= 0; return Column( children: [ _HoldingRow( coinCode: h.coinCode, quantity: double.tryParse(h.quantity)?.toStringAsFixed(4) ?? h.quantity, value: '${double.tryParse(h.currentValue)?.toStringAsFixed(2) ?? h.currentValue} USDT', profitRate: '${isProfit ? '+' : ''}${h.profitRate.toStringAsFixed(2)}%', isProfit: isProfit, ), if (index < holdings.length - 1) const _HoldingDivider(), ], ); }), ), ), ], ); } } /// Divider between holding rows — .pen node BCCbR / yejhE /// fill: $border-default, height: 1, opacity: 0.5 class _HoldingDivider extends StatelessWidget { const _HoldingDivider(); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Container( height: 1, margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), color: colorScheme.outlineVariant.withValues(alpha: 0.5), ); } } /// Holding row — matching .pen nodes dAt4j / eK6vq / jiSUK /// padding [14, 16], space_between layout /// Left: avatar circle (36x36, radius 18, fill $accent-light) + coin info (gap 2) /// Right: value + pnl (gap 2, align end) class _HoldingRow extends StatelessWidget { final String coinCode; final String quantity; final String value; final String profitRate; final bool isProfit; const _HoldingRow({ required this.coinCode, required this.quantity, required this.value, required this.profitRate, required this.isProfit, }); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final accentColor = isDark ? colorScheme.secondary : colorScheme.primary; final accentBgColor = accentColor.withValues(alpha: 0.1); final profitColor = isProfit ? AppColorScheme.getUpColor(isDark) : AppColorScheme.getDownColor(isDark); return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: 14), child: Row( children: [ // Avatar circle with first letter — .pen SJNDJ/EjSIN/3GQ5M Container( width: 36, height: 36, decoration: BoxDecoration( color: accentBgColor, borderRadius: BorderRadius.circular(18), ), alignment: Alignment.center, child: Text( coinCode.substring(0, 1), style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.w700, color: accentColor, ), ), ), const SizedBox(width: 10), // Coin name + quantity — .pen fivxJ/Kxv3d/5CsoQ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( coinCode, style: GoogleFonts.inter( fontSize: 14, fontWeight: FontWeight.w600, color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( quantity, style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.normal, color: colorScheme.onSurfaceVariant, ), ), ], ), ), // Value + profit rate — .pen vYJsU/2nLAg/IlWck Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Text( value, style: GoogleFonts.inter( fontSize: 13, fontWeight: FontWeight.w500, color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( profitRate, style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, color: profitColor, ), ), ], ), ], ), ); } } // ============================================ // Dialogs — kept from original with style updates // ============================================ void _showDepositDialog(BuildContext context) { final amountController = TextEditingController(); final formKey = GlobalKey(); final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '充值', style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.xs), Text( 'Asset: USDT', style: GoogleFonts.inter( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), ], ), Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(AppRadius.md), ), child: Icon( LucideIcons.wallet, color: colorScheme.secondary, ), ), ], ), const SizedBox(height: AppSpacing.lg), ShadForm( key: formKey, child: ShadInputFormField( id: 'amount', controller: amountController, label: const Text('充值金额'), placeholder: const Text('最低 1000 USDT'), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: (v) { if (v == null || v.isEmpty) return '请输入金额'; final n = double.tryParse(v); if (n == null || n <= 0) return '请输入有效金额'; if (n < 1000) return '单笔最低充值1000 USDT'; return null; }, ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '取消', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 48, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '下一步', type: NeonButtonType.primary, onPressed: () async { if (formKey.currentState!.saveAndValidate()) { Navigator.of(ctx).pop(); final response = await context.read().deposit( amount: amountController.text, ); if (context.mounted) { if (response.success && response.data != null) { _showDepositResultDialog(context, response.data!); } else { _showResultDialog(context, '申请失败', response.message); } } } }, height: 48, showGlow: true, ), ), ], ), ], ), ), ), ); } void _showDepositResultDialog(BuildContext context, Map data) { final orderNo = data['orderNo'] as String? ?? ''; final amount = data['amount']?.toString() ?? '0.00'; final walletAddress = data['walletAddress'] as String? ?? ''; final walletNetwork = data['walletNetwork'] as String? ?? 'TRC20'; final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ NeonIcon( icon: Icons.check_circle, color: AppColorScheme.getUpColor(isDark), size: 24, ), const SizedBox(width: AppSpacing.sm), Text( '充值申请成功', style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), ], ), const SizedBox(height: AppSpacing.lg), _InfoRow(label: '订单号', value: orderNo), const SizedBox(height: AppSpacing.sm), _InfoRow(label: '充值金额', value: '$amount USDT', isBold: true), const SizedBox(height: AppSpacing.lg), Text( '请向以下地址转账:', style: GoogleFonts.inter( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: AppSpacing.sm), _WalletAddressCard(address: walletAddress, network: walletNetwork), const SizedBox(height: AppSpacing.md), Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: AppColorScheme.warning.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all( color: AppColorScheme.warning.withValues(alpha: 0.2), ), ), child: Row( children: [ Icon(Icons.info_outline, size: 16, color: AppColorScheme.warning), const SizedBox(width: AppSpacing.sm), Expanded( child: Text( '转账完成后请点击"已打款"按钮确认', style: GoogleFonts.inter(fontSize: 12, color: AppColorScheme.warning), ), ), ], ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '稍后确认', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '已打款', type: NeonButtonType.primary, onPressed: () async { Navigator.of(ctx).pop(); final response = await context.read().confirmPay(orderNo); if (context.mounted) { _showResultDialog( context, response.success ? '确认成功' : '确认失败', response.success ? '请等待管理员审核' : response.message, ); } }, height: 44, showGlow: true, ), ), ], ), ], ), ), ), ); } class _InfoRow extends StatelessWidget { final String label; final String value; final bool isBold; const _InfoRow({required this.label, required this.value, this.isBold = false}); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: GoogleFonts.inter( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), Text( value, style: GoogleFonts.inter( fontSize: 12, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: colorScheme.onSurface, ), ), ], ); } } class _WalletAddressCard extends StatelessWidget { final String address; final String network; const _WalletAddressCard({required this.address, required this.network}); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all( color: colorScheme.outlineVariant.withValues(alpha: 0.3), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( address, style: const TextStyle( fontFamily: 'monospace', fontSize: 12, ), ), ), GestureDetector( onTap: () { Clipboard.setData(ClipboardData(text: address)); ToastUtils.show('地址已复制到剪贴板'); }, child: Container( padding: const EdgeInsets.all(AppSpacing.xs), decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Icon( LucideIcons.copy, size: 16, color: colorScheme.primary, ), ), ), ], ), const SizedBox(height: AppSpacing.sm), Text( '网络: $network', style: GoogleFonts.inter( fontSize: 11, color: colorScheme.onSurfaceVariant, ), ), ], ), ); } } void _showWithdrawDialog(BuildContext context, String? balance) { final amountController = TextEditingController(); final addressController = TextEditingController(); final contactController = TextEditingController(); final formKey = GlobalKey(); final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), ), child: Icon( LucideIcons.wallet, color: colorScheme.primary, ), ), const SizedBox(width: AppSpacing.sm), Text( '提现', style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), ], ), const SizedBox(height: AppSpacing.xs), Text( '安全地将您的资产转移到外部钱包地址', style: GoogleFonts.inter( fontSize: 12, color: colorScheme.onSurfaceVariant, ), ), if (balance != null) ...[ const SizedBox(height: AppSpacing.md), Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.sm, ), decoration: BoxDecoration( color: AppColorScheme.up.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.full), border: Border.all( color: AppColorScheme.up.withValues(alpha: 0.2), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '可用余额: ', style: GoogleFonts.inter( fontSize: 10, color: colorScheme.onSurfaceVariant, ), ), Text( '$balance USDT', style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.bold, color: AppColorScheme.up, ), ), ], ), ), ], const SizedBox(height: AppSpacing.lg), ShadForm( key: formKey, child: Column( children: [ ShadInputFormField( id: 'amount', controller: amountController, label: const Text('提现金额'), placeholder: const Text('请输入提现金额(USDT)'), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: Validators.amount, ), const SizedBox(height: AppSpacing.md), ShadInputFormField( id: 'address', controller: addressController, label: const Text('目标地址'), placeholder: const Text('请输入提现地址'), validator: (v) => Validators.required(v, '提现地址'), ), const SizedBox(height: AppSpacing.md), ShadInputFormField( id: 'contact', controller: contactController, label: const Text('联系方式(可选)'), placeholder: const Text('联系方式'), ), ], ), ), const SizedBox(height: AppSpacing.lg), Row( children: [ Expanded( child: NeonButton( text: '取消', type: NeonButtonType.outline, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: NeonButton( text: '提交', type: NeonButtonType.primary, onPressed: () async { if (formKey.currentState!.saveAndValidate()) { Navigator.of(ctx).pop(); final response = await context.read().withdraw( amount: amountController.text, withdrawAddress: addressController.text, withdrawContact: contactController.text.isNotEmpty ? contactController.text : null, ); if (context.mounted) { _showResultDialog( context, response.success ? '申请成功' : '申请失败', response.success ? '请等待管理员审批' : response.message, ); } } }, height: 44, showGlow: true, ), ), ], ), const SizedBox(height: AppSpacing.md), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.verified_user, size: 12, color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(width: AppSpacing.xs), Text( 'End-to-End Encrypted Transaction', style: GoogleFonts.inter( fontSize: 10, color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), ), ], ), ], ), ), ), ), ); } void _navigateToTransfer(BuildContext context) async { final result = await Navigator.push( context, MaterialPageRoute(builder: (_) => const TransferPage()), ); if (result == true && context.mounted) { context.read().refreshAll(force: true); } } void _showResultDialog(BuildContext context, String title, String? message) { final colorScheme = Theme.of(context).colorScheme; showShadDialog( context: context, builder: (ctx) => Dialog( backgroundColor: Colors.transparent, child: GlassPanel( borderRadius: BorderRadius.circular(AppRadius.lg), padding: const EdgeInsets.all(AppSpacing.lg), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( title, style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), ), if (message != null) ...[ const SizedBox(height: AppSpacing.sm), Text( message, style: GoogleFonts.inter(color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ], const SizedBox(height: AppSpacing.lg), SizedBox( width: double.infinity, child: NeonButton( text: '确定', type: NeonButtonType.primary, onPressed: () => Navigator.of(ctx).pop(), height: 44, showGlow: false, ), ), ], ), ), ), ); }