Files
monisuo/flutter_monisuo/lib/ui/pages/asset/asset_page.dart
2026-03-25 00:47:37 +08:00

1322 lines
42 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 '../../shared/ui_constants.dart';
import '../../components/glass_panel.dart';
import '../../components/neon_glow.dart';
import '../orders/fund_orders_page.dart';
/// 资产页面 - 钱包风格
class AssetPage extends StatefulWidget {
const AssetPage({super.key});
@override
State<AssetPage> createState() => _AssetPageState();
}
class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixin {
@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(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 1. 总资产估值卡片
_TotalAssetCard(overview: provider.overview),
SizedBox(height: AppSpacing.lg),
// 2. 操作按钮
_ActionButtons(provider: provider),
SizedBox(height: AppSpacing.lg),
// 3. 资产组合
_AssetComposition(provider: provider),
SizedBox(height: AppSpacing.lg),
// 4. 代币列表
_TokenList(holdings: provider.holdings),
],
),
),
);
},
),
);
}
}
/// 总资产估值卡片
class _TotalAssetCard extends StatelessWidget {
final dynamic overview;
const _TotalAssetCard({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.xl),
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(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'总资产估值',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
letterSpacing: 0.5,
color: Colors.white.withOpacity(0.7),
),
),
SizedBox(height: AppSpacing.sm),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'\$${overview?.totalAsset ?? '0.00'}',
style: GoogleFonts.spaceGrotesk(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(width: AppSpacing.sm),
Padding(
padding: EdgeInsets.only(bottom: 8),
child: Text(
'USDT',
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.7),
),
),
),
],
),
SizedBox(height: AppSpacing.md),
Row(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
vertical: AppSpacing.xs,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
LucideIcons.trendingUp,
color: Colors.white.withOpacity(0.9),
size: 12,
),
SizedBox(width: AppSpacing.xs),
Text(
'今日收益: ${overview?.totalProfit ?? '0.00'} USDT',
style: TextStyle(
fontSize: 11,
color: Colors.white.withOpacity(0.9),
),
),
],
),
),
],
),
],
),
);
}
}
/// 操作按钮
class _ActionButtons extends StatelessWidget {
final AssetProvider provider;
const _ActionButtons({required this.provider});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: _ActionButton(
icon: LucideIcons.download,
label: '充币',
onTap: () => _showDepositDialog(context),
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: _ActionButton(
icon: LucideIcons.upload,
label: '提币',
onTap: () => _showWithdrawDialog(context, provider.fundAccount?.balance),
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: _ActionButton(
icon: LucideIcons.arrowLeftRight,
label: '划转',
onTap: () => _showTransferDialog(context),
),
),
],
);
}
}
/// 操作按钮项
class _ActionButton extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onTap;
const _ActionButton({
required this.icon,
required this.label,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(vertical: AppSpacing.md),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.2),
),
),
child: Column(
children: [
Icon(
icon,
color: colorScheme.primary,
size: 24,
),
SizedBox(height: AppSpacing.xs),
Text(
label,
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
}
/// 资产组合
class _AssetComposition extends StatelessWidget {
final AssetProvider provider;
const _AssetComposition({required this.provider});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final fund = provider.fundAccount;
final overview = provider.overview;
final fundBalance = fund?.balance ?? overview?.fundBalance ?? '0.00';
final tradeBalance = overview?.tradeBalance ?? '0';
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'资产组合',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.md),
Row(
children: [
Expanded(
child: _AssetCard(
icon: LucideIcons.wallet,
title: '资金账户',
amount: '$fundBalance USDT',
onTap: () {},
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: _AssetCard(
icon: LucideIcons.trendingUp,
title: '交易账户',
amount: '$tradeBalance USDT',
onTap: () {},
),
),
],
),
],
);
}
}
/// 资产卡片
class _AssetCard extends StatelessWidget {
final IconData icon;
final String title;
final String amount;
final VoidCallback onTap;
const _AssetCard({
required this.icon,
required this.title,
required this.amount,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.2),
),
),
child: Column(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Center(
child: Icon(
icon,
color: colorScheme.primary,
size: 20,
),
),
),
SizedBox(height: AppSpacing.sm),
Text(
title,
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant,
),
),
SizedBox(height: AppSpacing.xs),
Text(
amount,
style: GoogleFonts.spaceGrotesk(
fontSize: 14,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
],
),
),
);
}
}
/// 资产列表
class _TokenList extends StatelessWidget {
final List holdings;
const _TokenList({required this.holdings});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
// 对持仓进行排序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();
// USDT 排在最前面
if (codeA == 'USDT') return -1;
if (codeB == 'USDT') return 1;
return 0;
});
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'资产列表',
style: GoogleFonts.spaceGrotesk(
fontSize: 16,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.md),
if (sortedHoldings.isEmpty)
_EmptyState(icon: LucideIcons.wallet, message: '暂无持仓')
else
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: sortedHoldings.length,
separatorBuilder: (_, __) => Divider(
height: 1,
color: colorScheme.outlineVariant.withOpacity(0.2),
),
itemBuilder: (context, index) => _TokenItem(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 _TokenItem extends StatelessWidget {
final dynamic holding;
const _TokenItem({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.md),
child: Row(
children: [
Container(
width: 44,
height: 44,
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,
fontSize: 18,
),
),
),
),
SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
holding.coinCode,
style: GoogleFonts.spaceGrotesk(
fontSize: 15,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
SizedBox(width: AppSpacing.sm),
Container(
padding: EdgeInsets.symmetric(
horizontal: AppSpacing.xs,
vertical: 2,
),
decoration: BoxDecoration(
color: AppColorScheme.getUpColor(isDark).withOpacity(0.1),
borderRadius: BorderRadius.circular(AppRadius.sm),
),
child: Text(
'+5.2%',
style: TextStyle(
fontSize: 10,
color: AppColorScheme.getUpColor(isDark),
fontWeight: FontWeight.w600,
),
),
),
],
),
SizedBox(height: AppSpacing.xs),
Text(
holding.quantity,
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${holding.currentValue} USDT',
style: GoogleFonts.spaceGrotesk(
fontSize: 15,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.xs),
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.xxl),
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(
'充币',
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.xxl),
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) {
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.xxl),
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(
'提币',
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 _showTransferDialog(BuildContext context) {
final controller = TextEditingController();
final formKey = GlobalKey<ShadFormState>();
int direction = 1;
final colorScheme = Theme.of(context).colorScheme;
showShadDialog(
context: context,
builder: (ctx) => StatefulBuilder(
builder: (ctx, setState) => Dialog(
backgroundColor: Colors.transparent,
child: GlassPanel(
borderRadius: BorderRadius.circular(AppRadius.xxl),
padding: EdgeInsets.all(AppSpacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'划转',
style: GoogleFonts.spaceGrotesk(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colorScheme.onSurface,
),
),
SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: _DirectionButton(
label: '资金→交易',
isSelected: direction == 1,
onTap: () => setState(() => direction = 1),
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: _DirectionButton(
label: '交易→资金',
isSelected: direction == 2,
onTap: () => setState(() => direction = 2),
),
),
],
),
SizedBox(height: AppSpacing.lg),
ShadForm(
key: formKey,
child: ShadInputFormField(
id: 'amount',
controller: controller,
label: const Text('划转金额'),
placeholder: const Text('请输入划转金额(USDT)'),
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: 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>().transfer(
direction: direction,
amount: controller.text,
);
if (context.mounted) {
_showResultDialog(
context,
response.success ? '划转成功' : '划转失败',
response.message,
);
}
}
},
height: 44,
showGlow: true,
),
),
],
),
],
),
),
),
),
);
}
class _DirectionButton extends StatelessWidget {
final String label;
final bool isSelected;
final VoidCallback onTap;
const _DirectionButton({
required this.label,
required this.isSelected,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: isSelected
? colorScheme.primary.withOpacity(0.15)
: colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
border: isSelected
? Border.all(
color: colorScheme.primary.withOpacity(0.3),
)
: null,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (isSelected)
Icon(
LucideIcons.check,
size: 14,
color: colorScheme.primary,
)
else
SizedBox(width: 14),
SizedBox(width: AppSpacing.xs),
Text(
label,
style: TextStyle(
color: isSelected
? colorScheme.primary
: colorScheme.onSurfaceVariant,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
fontSize: 12,
),
),
],
),
),
),
);
}
}
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.xxl),
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,
),
),
],
),
),
),
);
}