import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/utils/number_format.dart'; import '../../../providers/asset_provider.dart'; import '../../../providers/spot_coin_cache_provider.dart'; import '../../../providers/spot_provider.dart' show SpotWalletAsset; import '../../widgets/common/app_refresh_indicator.dart'; import '../../widgets/common/coin_icon.dart'; /// 资金 Tab — 显示 SPOT 资金账户余额 class AssetSpotTab extends StatelessWidget { const AssetSpotTab({super.key, required this.state, required this.notifier}); final AssetState state; final AssetNotifier notifier; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final obscure = state.obscureBalance; final hideZero = state.hideZeroBalanceInFundTab; final spotBalance = state.walletBalance('SPOT').toDouble(); final display = obscure ? '******' : formatPrice(spotBalance, decimalPlaces: 2); final assets = state.fundWallets; return AppRefreshIndicator( onRefresh: notifier.silentRefresh, child: ListView( children: [ // 资金账户余额 Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text(AppLocalizations.of(context)!.fundAccount, style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 13)), const SizedBox(width: 6), GestureDetector( onTap: notifier.toggleObscure, child: Icon( obscure ? Icons.visibility_off_outlined : Icons.visibility_outlined, size: 16, color: cs.onSurface.withAlpha(153), ), ), ], ), const SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(display, style: TextStyle(color: cs.onSurface, fontSize: 32, fontWeight: FontWeight.w700, letterSpacing: -0.5)), const SizedBox(width: 6), Padding( padding: const EdgeInsets.only(bottom: 5), child: Text('USDT', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 14)), ), ], ), ], ), ), // 充币 + 提币 + 划转 Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), child: Row( children: [ Expanded( child: GestureDetector( onTap: () => context.push('/asset/deposit'), child: Container( height: 44, decoration: BoxDecoration(color: AppColors.brand, borderRadius: BorderRadius.circular(22)), child: Center(child: Text(AppLocalizations.of(context)!.depositCoin, style: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.w600))), ), ), ), const SizedBox(width: 10), Expanded( child: GestureDetector( onTap: () => context.push('/asset/withdraw'), child: Container( height: 44, decoration: BoxDecoration(color: AppColors.brand, borderRadius: BorderRadius.circular(22)), child: Center(child: Text(AppLocalizations.of(context)!.withdrawCoin, style: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.w600))), ), ), ), const SizedBox(width: 10), Expanded( child: GestureDetector( onTap: () => context.push('/asset/transfer?from=SPOT&to=SPOT_TRADING'), child: Container( height: 44, decoration: BoxDecoration(color: AppColors.brand, borderRadius: BorderRadius.circular(22)), child: Center(child: Text(AppLocalizations.of(context)!.transfer, style: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.w600))), ), ), ), ], ), ), Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: notifier.toggleHideZeroBalanceInFundTab, child: Row( children: [ Icon( hideZero ? Icons.check_box_outlined : Icons.check_box_outline_blank, size: 18, color: hideZero ? AppColors.brand : cs.onSurface.withAlpha(140), ), const SizedBox(width: 6), Text( AppLocalizations.of(context)!.hideZeroBalanceAssets, style: TextStyle( color: cs.onSurface.withAlpha(170), fontSize: 13, ), ), ], ), ), ], ), ), const SizedBox(height: 8), if (assets.isEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 36), child: Center( child: Text( AppLocalizations.of(context)!.noAssets, style: TextStyle( color: cs.onSurface.withAlpha(140), fontSize: 13, ), ), ), ) else for (final asset in assets) _FundAssetRow(asset: asset, obscure: obscure), const SizedBox(height: 24), ], ), ); } } class _FundAssetRow extends ConsumerWidget { const _FundAssetRow({required this.asset, required this.obscure}); final SpotWalletAsset asset; final bool obscure; @override Widget build(BuildContext context, WidgetRef ref) { final cs = Theme.of(context).colorScheme; final l10n = AppLocalizations.of(context)!; final value = obscure ? '******' : null; final mapState = ref.watch(spotCoinCacheProvider); final iconUrl = spotCoinIconUrl(mapState, asset.coin); return Container( padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), decoration: BoxDecoration( border: Border( bottom: BorderSide(color: cs.outline.withAlpha(40), width: 0.6), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ CoinIcon( symbol: asset.coin, iconUrl: iconUrl, size: 40, shape: BoxShape.circle, ), const SizedBox(width: 10), Text( asset.coin, style: TextStyle( color: cs.onSurface, fontSize: 16, fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: 14), Row( children: [ Expanded( child: _FundAssetCell( label: l10n.assetBalance, value: value ?? formatAmount(asset.total, decimals: 6), align: CrossAxisAlignment.start, ), ), Expanded( child: _FundAssetCell( label: l10n.availableLabel, value: value ?? formatAmount(asset.balance, decimals: 6), align: CrossAxisAlignment.center, ), ), Expanded( child: _FundAssetCell( label: l10n.unavailableLabel, value: value ?? formatAmount(asset.frozenBalance, decimals: 6), align: CrossAxisAlignment.end, ), ), ], ), ], ), ); } } class _FundAssetCell extends StatelessWidget { const _FundAssetCell({ required this.label, required this.value, required this.align, }); final String label; final String value; final CrossAxisAlignment align; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final textAlign = align == CrossAxisAlignment.start ? TextAlign.left : align == CrossAxisAlignment.end ? TextAlign.right : TextAlign.center; return Column( crossAxisAlignment: align, children: [ Text( label, style: TextStyle(color: cs.onSurface.withAlpha(140), fontSize: 11), textAlign: textAlign, ), const SizedBox(height: 4), Text( value, style: TextStyle( color: cs.onSurface, fontSize: 13, fontWeight: FontWeight.w600, fontFeatures: const [FontFeature.tabularFigures()], ), textAlign: textAlign, ), ], ); } }