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/currency_provider.dart'; import '../../widgets/common/app_refresh_indicator.dart'; import 'asset_screen.dart' show AssetAccountRow; /// 总览 Tab class AssetOverviewTab extends ConsumerWidget { const AssetOverviewTab( {super.key, required this.state, required this.notifier}); final AssetState state; final AssetNotifier notifier; @override Widget build(BuildContext context, WidgetRef ref) { ref.watch(currencyProvider); // 法币切换时触发重建 final cs = Theme.of(context).colorScheme; final total = state.totalUsdtValue; final display = state.obscureBalance ? '******' : formatPrice(total, decimalPlaces: 2); final pnl = state.todayPnl; final pnlRevenue = pnl?.revenue.toDouble() ?? 0; final double? pnlRate = pnl?.revenueRate?.toDouble(); final pnlSign = pnlRevenue >= 0 ? '+' : ''; final pnlColor = pnl?.isUpTrend == true ? AppColors.rise : pnl?.isUpTrend == false ? AppColors.fall : cs.onSurface.withAlpha(153); 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)!.assetValuation, style: TextStyle( color: cs.onSurface.withAlpha(153), fontSize: 13)), const SizedBox(width: 6), GestureDetector( onTap: notifier.toggleObscure, child: Icon( state.obscureBalance ? 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)), ), ], ), const SizedBox(height: 6), // 今日盈亏 Text( state.obscureBalance ? '${AppLocalizations.of(context)!.todayPnl} ******' : pnlRate != null ? '${AppLocalizations.of(context)!.todayPnl} $pnlSign$fiatSymbol${(pnlRevenue * fiatRate).toStringAsFixed(2)} ($pnlSign${(pnlRate * 100).toStringAsFixed(2)}%)' : '${AppLocalizations.of(context)!.todayPnl} $pnlSign$fiatSymbol${(pnlRevenue * fiatRate).toStringAsFixed(2)} (--)', style: TextStyle( color: pnlColor, fontSize: 13, fontWeight: FontWeight.w500), ), ], ), ), // 快捷操作 Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), child: Row( children: [ Expanded( child: _ActionChip( label: AppLocalizations.of(context)!.recharge, onTap: () => context.push('/asset/deposit'), ), ), const SizedBox(width: 8), Expanded( child: _ActionChip( label: AppLocalizations.of(context)!.withdraw, onTap: () => context.push('/asset/withdraw'), ), ), const SizedBox(width: 8), Expanded( child: _ActionChip( label: AppLocalizations.of(context)!.transfer, onTap: () => context.push('/asset/transfer'), ), ), const SizedBox(width: 8), Expanded( child: _ActionChip( label: AppLocalizations.of(context)!.fundHistory, onTap: () => context.push('/asset/history'), ), ), ], ), ), const SizedBox(height: 24), _AccountSection(state: state), const SizedBox(height: 32), ], ), ); } } class _ActionChip extends StatelessWidget { const _ActionChip({required this.label, required this.onTap}); final String label; final VoidCallback onTap; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; return GestureDetector( onTap: onTap, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), decoration: BoxDecoration( color: isDark ? AppColors.darkBgTertiary : AppColors.lightBgTertiary, borderRadius: BorderRadius.circular(20), ), alignment: Alignment.center, child: FittedBox( fit: BoxFit.scaleDown, child: Text( label, maxLines: 1, overflow: TextOverflow.visible, textAlign: TextAlign.center, style: TextStyle( color: cs.onSurface, fontSize: 13, fontWeight: FontWeight.w600, ), ), ), ), ); } } class _AccountSection extends StatelessWidget { const _AccountSection({required this.state}); final AssetState state; String _fmtUsdt(double bal, bool obscure) => obscure ? '******' : '${formatPrice(bal, decimalPlaces: 2)} USDT'; String _fmtUsd(double bal, bool obscure) => obscure ? '******' : formatFiatPrice(bal, pricePrecision: 2); String _fmtIbit(String amount, bool obscure) { if (obscure) { return '******'; } final parsed = double.tryParse(amount) ?? 0; return '${formatPrice(parsed, decimalPlaces: 2)} iBit'; } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final obscure = state.obscureBalance; final l10n = AppLocalizations.of(context)!; final swap = state.walletBalance('SWAP').toDouble(); final follow = state.walletBalance('FOLLOW').toDouble(); final fund = state.walletBalance('SPOT').toDouble(); final spotTrading = state.spotTradingTotal; final stakingLocked = state.stakingOverviewLocked; final rows = <({ IconData icon, String label, String amount, String usdAmount, })>[ ( icon: Icons.account_balance_wallet_outlined, label: l10n.fundAccount, amount: _fmtUsdt(fund, obscure), usdAmount: _fmtUsd(fund, obscure), ), ( icon: Icons.currency_exchange_outlined, label: l10n.spotTradingAccount, amount: _fmtUsdt(spotTrading, obscure), usdAmount: _fmtUsd(spotTrading, obscure), ), ( icon: Icons.show_chart, label: l10n.futuresAccount, amount: _fmtUsdt(swap, obscure), usdAmount: _fmtUsd(swap, obscure), ), ( icon: Icons.people_alt_outlined, label: l10n.copyAccount, amount: _fmtUsdt(follow, obscure), usdAmount: _fmtUsd(follow, obscure), ), ( icon: Icons.lock_outline, label: l10n.assetsLockedStakingAccount, amount: _fmtIbit(stakingLocked, obscure), usdAmount: '', ), ]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l10n.account, style: TextStyle( color: cs.onSurface, fontSize: 15, fontWeight: FontWeight.w600)), const SizedBox(height: 12), for (final row in rows) ...[ AssetAccountRow( icon: row.icon, label: row.label, amount: row.amount, usdAmount: row.usdAmount, ), const SizedBox(height: 8), ], ], ), ); } }