Files
monisuo/flutter_monisuo/lib/ui/pages/asset/asset_page.dart
sion 8825fe5b27 style: 完成前端专业金融风格优化
- 主题配色: 黑金传奇(暗) + 白金殿堂(亮)
- 底部导航: 去掉毛玻璃,简洁专业
- 交易页面: 明亮模式颜色优化,实心按钮
- 按钮圆角: xxl(24px) → lg(12px)/md(8px)
- 字体系统: Inter(币安同款)
- 整体风格: 专业金融科技
2026-03-30 03:31:42 +08:00

1232 lines
40 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.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';
import '../mine/kyc_page.dart';
/// 资产页面 - Material Design 3 风格
class AssetPage extends StatefulWidget {
const AssetPage({super.key});
@override
State<AssetPage> createState() => _AssetPageState();
}
class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixin {
int _activeTab = 0;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
// 强制刷新数据,确保加载最新数据
WidgetsBinding.instance.addPostFrameCallback((_) => _loadData());
}
void _loadData() {
// 强制刷新,不使用缓存
context.read<AssetProvider>().refreshAll(force: true);
}
@override
Widget build(BuildContext context) {
super.build(context);
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colorScheme.background,
body: Consumer<AssetProvider>(
builder: (context, provider, _) {
return RefreshIndicator(
onRefresh: () => provider.refreshAll(force: true),
color: colorScheme.primary,
backgroundColor: colorScheme.surfaceContainerHighest,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: AppSpacing.pagePadding,
child: Column(
children: [
_TabSelector(
tabs: const ['资金账户', '交易账户'],
selectedIndex: _activeTab,
onChanged: (index) => setState(() => _activeTab = index),
),
SizedBox(height: AppSpacing.md),
_activeTab == 0
? _FundAccountCard(provider: provider)
: _TradeAccountCard(
holdings: provider.holdings,
tradeBalance: provider.overview?.tradeBalance,
),
],
),
),
);
},
),
);
}
}
/// 资产总览卡片 - Material Design 3 风格
class _AssetCard extends StatelessWidget {
final dynamic overview;
const _AssetCard({required this.overview});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
width: double.infinity,
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.sm),
decoration: BoxDecoration(
gradient: AppColorScheme.assetCardGradient,
borderRadius: BorderRadius.circular(AppRadius.xl),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(isDark ? 0.15 : 0.08),
blurRadius: 20,
),
],
),
child: Column(
children: [
Text(
'PORTFOLIO VALUE',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
color: Colors.white.withOpacity(0.7),
),
),
SizedBox(height: AppSpacing.sm),
Text(
'\$${overview?.totalAsset ?? '0.00'}',
style: GoogleFonts.spaceGrotesk(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: AppSpacing.md),
Container(
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.xs + AppSpacing.xs,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
LucideIcons.trendingUp,
color: Colors.white.withOpacity(0.7),
size: 14,
),
SizedBox(width: AppSpacing.xs),
Text(
'总盈亏: ${overview?.totalProfit ?? '0.00'} USDT',
style: TextStyle(
fontSize: 12,
color: Colors.white.withOpacity(0.7),
),
),
],
),
),
],
),
);
}
}
/// Tab 选择器 - Material Design 3 风格
class _TabSelector extends StatelessWidget {
final List<String> tabs;
final int selectedIndex;
final ValueChanged<int> onChanged;
const _TabSelector({
required this.tabs,
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(
padding: EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(AppRadius.lg),
),
child: Row(
children: tabs.asMap().entries.map((entry) {
final index = entry.key;
final label = entry.value;
final isSelected = index == selectedIndex;
return Expanded(
child: GestureDetector(
onTap: () => onChanged(index),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: isSelected ? colorScheme.primary : Colors.transparent,
borderRadius: BorderRadius.circular(AppRadius.md),
boxShadow: isSelected
? [
BoxShadow(
color: colorScheme.primary.withOpacity(isDark ? 0.15 : 0.08),
blurRadius: 10,
),
]
: null,
),
child: Center(
child: Text(
label,
style: TextStyle(
color: isSelected ? colorScheme.onPrimary : colorScheme.onSurfaceVariant,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
),
),
),
);
}).toList(),
),
);
}
}
/// 资金账户卡片 - Glass Panel 风格
class _FundAccountCard extends StatelessWidget {
final AssetProvider provider;
const _FundAccountCard({required this.provider});
@override
Widget build(BuildContext context) {
final fund = provider.fundAccount;
final overview = provider.overview;
final colorScheme = Theme.of(context).colorScheme;
// 优先使用fund数据如果为null则使用overview的fundBalance
final displayBalance = fund?.balance ?? overview?.fundBalance ?? '0.00';
return GlassPanel(
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.xs),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'USDT 余额',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const FundOrdersPage()),
),
child: Row(
children: [
Text(
'充提记录',
style: TextStyle(
color: colorScheme.primary,
fontSize: 12,
),
),
Icon(
LucideIcons.chevronRight,
size: 14,
color: colorScheme.primary,
),
],
),
),
],
),
SizedBox(height: AppSpacing.sm),
Text(
displayBalance,
style: GoogleFonts.spaceGrotesk(
fontSize: 28,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: NeonButton(
text: '充值',
type: NeonButtonType.tertiary,
icon: Icons.add,
onPressed: () => _showDepositDialog(context),
height: 44,
showGlow: false,
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: NeonButton(
text: '提现',
type: NeonButtonType.secondary,
icon: Icons.remove,
onPressed: () => _showWithdrawDialog(context, fund?.balance),
height: 44,
showGlow: false,
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: NeonButton(
text: '划转',
type: NeonButtonType.outline,
icon: Icons.swap_horiz,
onPressed: () => _navigateToTransfer(context),
height: 44,
showGlow: false,
),
),
],
),
],
),
);
}
}
/// 交易账户卡片 - Glass Panel 风格
class _TradeAccountCard extends StatelessWidget {
final List holdings;
final String? tradeBalance;
const _TradeAccountCard({required this.holdings, this.tradeBalance});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
// 计算总市值所有持仓折算成USDT
double totalValue = 0;
for (var h in holdings) {
final value = double.tryParse(h.currentValue?.toString() ?? '0') ?? 0;
totalValue += value;
}
// 对持仓进行排序USDT 放在最上面
final sortedHoldings = List.from(holdings);
sortedHoldings.sort((a, b) {
final codeA = (a.coinCode ?? a['coinCode'] ?? '').toString().toUpperCase();
final codeB = (b.coinCode ?? b['coinCode'] ?? '').toString().toUpperCase();
if (codeA == 'USDT') return -1;
if (codeB == 'USDT') return 1;
return 0;
});
return GlassPanel(
padding: AppSpacing.cardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题行
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
LucideIcons.trendingUp,
size: 18,
color: colorScheme.primary,
),
),
SizedBox(width: AppSpacing.sm),
Text(
'交易账户',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
],
),
Icon(
LucideIcons.chevronRight,
size: 14,
color: colorScheme.primary,
),
],
),
SizedBox(height: AppSpacing.md),
// 余额
Text(
'余额 (USDT)',
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant,
),
),
SizedBox(height: AppSpacing.xs),
Text(
totalValue.toStringAsFixed(2),
style: GoogleFonts.spaceGrotesk(
fontSize: 28,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.lg),
// 持仓列表标题
Text(
'持仓列表',
style: GoogleFonts.spaceGrotesk(
fontSize: 14,
fontWeight: FontWeight.w600,
color: colorScheme.onSurfaceVariant,
),
),
SizedBox(height: AppSpacing.md),
if (sortedHoldings.isEmpty)
const _EmptyState(icon: LucideIcons.wallet, message: '暂无持仓')
else
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: sortedHoldings.length,
separatorBuilder: (_, __) => Container(
margin: EdgeInsets.only(left: 56),
height: 1,
color: AppColorScheme.glassPanelBorder,
),
itemBuilder: (context, index) => _HoldingItem(holding: sortedHoldings[index]),
),
],
),
);
}
}
/// 空状态
class _EmptyState extends StatelessWidget {
final IconData icon;
final String message;
const _EmptyState({required this.icon, required this.message});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Center(
child: Padding(
padding: EdgeInsets.all(AppSpacing.xl),
child: Column(
children: [
Icon(
icon,
size: 48,
color: colorScheme.onSurfaceVariant,
),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text(
message,
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
],
),
),
);
}
}
/// 持仓项
class _HoldingItem extends StatelessWidget {
final dynamic holding;
const _HoldingItem({required this.holding});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Padding(
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Center(
child: Text(
holding.coinCode.substring(0, 1),
style: TextStyle(
color: colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
holding.coinCode,
style: GoogleFonts.spaceGrotesk(
fontSize: 14,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
Text(
'数量: ${holding.quantity}',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${holding.currentValue} USDT',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurface,
),
),
Text(
holding.formattedProfitRate,
style: TextStyle(
color: holding.isProfit ? AppColorScheme.getUpColor(isDark) : AppColorScheme.down,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
}
// ============================================
// Dialogs - Glass Panel 风格
// ============================================
void _showDepositDialog(BuildContext context) {
final amountController = TextEditingController();
final formKey = GlobalKey<ShadFormState>();
final colorScheme = Theme.of(context).colorScheme;
showShadDialog(
context: context,
builder: (ctx) => Dialog(
backgroundColor: Colors.transparent,
child: GlassPanel(
borderRadius: BorderRadius.circular(AppRadius.lg),
padding: EdgeInsets.all(AppSpacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Deposit (充值)',
style: GoogleFonts.spaceGrotesk(
fontSize: 24,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.xs),
Text(
'Asset: USDT',
style: TextStyle(
fontSize: 12,
letterSpacing: 0.1,
color: colorScheme.onSurfaceVariant,
),
),
],
),
Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
LucideIcons.wallet,
color: colorScheme.secondary,
),
),
],
),
SizedBox(height: AppSpacing.lg),
ShadForm(
key: formKey,
child: ShadInputFormField(
id: 'amount',
controller: amountController,
label: const Text('充值金额'),
placeholder: const Text('0.00'),
keyboardType: const TextInputType.numberWithOptions(decimal: true),
validator: Validators.amount,
),
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: NeonButton(
text: '取消',
type: NeonButtonType.outline,
onPressed: () => Navigator.of(ctx).pop(),
height: 48,
showGlow: false,
),
),
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<AssetProvider>().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<String, dynamic> 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: 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,
),
SizedBox(width: AppSpacing.sm),
Text(
'充值申请成功',
style: GoogleFonts.spaceGrotesk(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
],
),
SizedBox(height: AppSpacing.lg),
_InfoRow(label: '订单号', value: orderNo),
SizedBox(height: AppSpacing.sm),
_InfoRow(label: '充值金额', value: '$amount USDT', isBold: true),
SizedBox(height: AppSpacing.lg),
Text(
'请向以下地址转账:',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
SizedBox(height: AppSpacing.sm),
_WalletAddressCard(address: walletAddress, network: walletNetwork),
SizedBox(height: AppSpacing.md),
Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: AppColorScheme.warning.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: AppColorScheme.warning.withOpacity(0.2),
),
),
child: Row(
children: [
Icon(Icons.info_outline, size: 16, color: AppColorScheme.warning),
SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
'转账完成后请点击"已打款"按钮确认',
style: TextStyle(fontSize: 12, color: AppColorScheme.warning),
),
),
],
),
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: NeonButton(
text: '稍后确认',
type: NeonButtonType.outline,
onPressed: () => Navigator.of(ctx).pop(),
height: 44,
showGlow: false,
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: NeonButton(
text: '已打款',
type: NeonButtonType.primary,
onPressed: () async {
Navigator.of(ctx).pop();
final response = await context.read<AssetProvider>().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: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
Text(
value,
style: TextStyle(
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: EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.3),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
address,
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: colorScheme.onSurface,
),
),
),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: address));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('地址已复制到剪贴板')),
);
},
child: Container(
padding: EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
child: Icon(
LucideIcons.copy,
size: 16,
color: colorScheme.primary,
),
),
),
],
),
SizedBox(height: AppSpacing.sm),
Text(
'网络: $network',
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
}
void _showWithdrawDialog(BuildContext context, String? balance) {
// KYC校验未完成实名认证则引导去认证
final auth = context.read<AuthProvider>();
if (auth.user?.kycStatus != 2) {
_showKycRequiredDialog(context);
return;
}
final amountController = TextEditingController();
final addressController = TextEditingController();
final contactController = TextEditingController();
final formKey = GlobalKey<ShadFormState>();
final colorScheme = Theme.of(context).colorScheme;
showShadDialog(
context: context,
builder: (ctx) => Dialog(
backgroundColor: Colors.transparent,
child: GlassPanel(
borderRadius: BorderRadius.circular(AppRadius.lg),
padding: EdgeInsets.all(AppSpacing.lg),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(
LucideIcons.wallet,
color: colorScheme.primary,
),
),
SizedBox(width: AppSpacing.sm),
Text(
'提现 (Withdraw)',
style: GoogleFonts.spaceGrotesk(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
],
),
SizedBox(height: AppSpacing.xs),
Text(
'Securely transfer your assets to an external wallet address.',
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
if (balance != null) ...[
SizedBox(height: AppSpacing.md),
Container(
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColorScheme.up.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: AppColorScheme.up.withOpacity(0.2),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'可用余额: ',
style: TextStyle(
fontSize: 10,
letterSpacing: 0.1,
color: colorScheme.onSurfaceVariant,
),
),
Text(
'$balance USDT',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: AppColorScheme.up,
),
),
],
),
),
],
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,
),
SizedBox(height: AppSpacing.md),
ShadInputFormField(
id: 'address',
controller: addressController,
label: const Text('目标地址'),
placeholder: const Text('请输入提现地址'),
validator: (v) => Validators.required(v, '提现地址'),
),
SizedBox(height: AppSpacing.md),
ShadInputFormField(
id: 'contact',
controller: contactController,
label: const Text('联系方式(可选)'),
placeholder: const Text('联系方式'),
),
],
),
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: NeonButton(
text: '取消',
type: NeonButtonType.outline,
onPressed: () => Navigator.of(ctx).pop(),
height: 44,
showGlow: false,
),
),
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<AssetProvider>().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,
),
),
],
),
SizedBox(height: AppSpacing.md),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.verified_user,
size: 12,
color: colorScheme.onSurfaceVariant.withOpacity(0.5),
),
SizedBox(width: AppSpacing.xs),
Text(
'End-to-End Encrypted Transaction',
style: TextStyle(
fontSize: 10,
letterSpacing: 0.1,
color: colorScheme.onSurfaceVariant.withOpacity(0.5),
),
),
],
),
],
),
),
),
),
);
}
void _navigateToTransfer(BuildContext context) async {
final result = await Navigator.push<bool>(
context,
MaterialPageRoute(builder: (_) => const TransferPage()),
);
// 如果划转成功,刷新数据
if (result == true && context.mounted) {
context.read<AssetProvider>().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: EdgeInsets.all(AppSpacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(title,
style: GoogleFonts.spaceGrotesk(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
)),
if (message != null) ...[
SizedBox(height: AppSpacing.sm),
Text(message,
style: TextStyle(color: colorScheme.onSurfaceVariant),
textAlign: TextAlign.center),
],
SizedBox(height: AppSpacing.lg),
SizedBox(
width: double.infinity,
child: NeonButton(
text: '确定',
type: NeonButtonType.primary,
onPressed: () => Navigator.of(ctx).pop(),
height: 44,
showGlow: false,
),
),
],
),
),
),
);
}
void _showKycRequiredDialog(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
showShadDialog(
context: context,
builder: (ctx) => Dialog(
backgroundColor: Colors.transparent,
child: GlassPanel(
borderRadius: BorderRadius.circular(AppRadius.lg),
padding: EdgeInsets.all(AppSpacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColorScheme.warning.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.shieldAlert,
color: AppColorScheme.warning,
size: 32,
),
),
SizedBox(height: AppSpacing.md),
Text(
'需要实名认证',
style: GoogleFonts.spaceGrotesk(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.sm),
Text(
'提现前请先完成实名认证,保障账户安全',
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: 14,
),
textAlign: TextAlign.center,
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: NeonButton(
text: '取消',
type: NeonButtonType.outline,
onPressed: () => Navigator.of(ctx).pop(),
height: 44,
showGlow: false,
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: NeonButton(
text: '去认证',
type: NeonButtonType.primary,
onPressed: () {
Navigator.of(ctx).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const KycPage(returnToWithdraw: true),
),
);
},
height: 44,
showGlow: true,
),
),
],
),
],
),
),
),
);
}