| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- 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),
- ],
- ],
- ),
- );
- }
- }
|