docs: 删除过时的功能规格文档

feat(ui): 重构资产页面UI,移除shadcn_ui依赖并简化设计

- 删除三个过时的功能规格文档(apply-new-styles.md、bottom-nav-labels.md、theme-dynamic-colors.md)
- 重构充值页面(deposit_page.dart):移除shadcn_ui依赖,简化表单验证和UI设计,使用动态主题颜色
- 重构划转页面(transfer_page.dart):移除复杂动画和shadcn_ui依赖,简化UI布局和交互逻辑
- 重构提现页面(withdraw_page.dart):移除shadcn_ui依赖,简化表单验证和网络选择器
- 重构我的页面相关组件:统一使用动态主题颜色,简化菜单项设计和KYC状态显示
- 所有页面现在使用Theme.of(context)获取动态颜色,支持明暗主题切换
- 移除硬编码的颜色引用,提高代码可维护性和主题一致性
This commit is contained in:
2026-04-08 01:47:51 +08:00
parent 007915b6f2
commit dc6a8afc9a
13 changed files with 1271 additions and 2074 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:provider/provider.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/theme/app_theme_extension.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/utils/toast_utils.dart';
import '../../../providers/asset_provider.dart';
import '../../shared/ui_constants.dart';
/// 提現頁面 — 獨立頁面,替代彈窗
/// 提现页面
class WithdrawPage extends StatefulWidget {
final String? balance;
@@ -24,17 +21,16 @@ class _WithdrawPageState extends State<WithdrawPage> {
final _amountController = TextEditingController();
final _addressController = TextEditingController();
final _contactController = TextEditingController();
final _formKey = GlobalKey<ShadFormState>();
final _amountFocus = FocusNode();
bool _isSubmitting = false;
final _feeNotifier = ValueNotifier<String>('提現將扣除 10% 手續費');
final _networksNotifier = ValueNotifier<List<String>>([]);
final _selectedNetworkNotifier = ValueNotifier<String?>(null);
List<String> _networks = [];
String? _selectedNetwork;
@override
void initState() {
super.initState();
_amountController.addListener(_onAmountChanged);
_amountController.addListener(() => setState(() {}));
_loadNetworks();
}
@@ -43,367 +39,468 @@ class _WithdrawPageState extends State<WithdrawPage> {
_amountController.dispose();
_addressController.dispose();
_contactController.dispose();
_feeNotifier.dispose();
_networksNotifier.dispose();
_selectedNetworkNotifier.dispose();
_amountFocus.dispose();
super.dispose();
}
void _onAmountChanged() {
final amount = double.tryParse(_amountController.text) ?? 0;
if (amount > 0) {
final fee = amount * 0.1;
final receivable = amount - fee;
_feeNotifier.value =
'手續費(10%): -${fee.toStringAsFixed(2)} USDT\n應收款: ${receivable.toStringAsFixed(2)} USDT';
} else {
_feeNotifier.value = '提現將扣除 10% 手續費';
}
}
// ============================================
// 业务逻辑
// ============================================
void _loadNetworks() {
context.read<AssetProvider>().getWalletNetworks().then((list) {
if (mounted) {
_networksNotifier.value = list;
if (list.isNotEmpty) {
_selectedNetworkNotifier.value = list.first;
}
setState(() {
_networks = list;
if (list.isNotEmpty) _selectedNetwork = list.first;
});
}
});
}
String get _feeText {
final amount = double.tryParse(_amountController.text) ?? 0;
if (amount > 0) {
final fee = amount * 0.1;
final receivable = amount - fee;
return '手续费(10%): -${fee.toStringAsFixed(2)} USDT | 应收款: ${receivable.toStringAsFixed(2)} USDT';
}
return '提现将扣除 10% 手续费';
}
Future<void> _submitWithdraw() async {
if (!_formKey.currentState!.saveAndValidate()) return;
final amount = double.tryParse(_amountController.text);
if (amount == null || amount <= 0) {
ToastUtils.showError('请输入有效金额');
return;
}
final address = _addressController.text.trim();
if (address.isEmpty) {
ToastUtils.showError('请输入提现地址');
return;
}
if (_isSubmitting) return;
setState(() => _isSubmitting = true);
try {
final response = await context.read<AssetProvider>().withdraw(
amount: _amountController.text,
withdrawAddress: _addressController.text,
withdrawContact:
_contactController.text.isNotEmpty ? _contactController.text : null,
network: _selectedNetworkNotifier.value,
withdrawAddress: address,
withdrawContact: _contactController.text.trim().isNotEmpty
? _contactController.text.trim()
: null,
network: _selectedNetwork,
);
if (!mounted) return;
if (response.success) {
ToastUtils.showSuccess('成功,等待管理員審');
ToastUtils.showSuccess('成功,等待管理员审');
Navigator.of(context).pop(true);
} else {
ToastUtils.showError(response.message ?? '請失敗');
ToastUtils.showError(response.message ?? '请失败');
}
} catch (e) {
if (mounted) ToastUtils.showError('請失敗: $e');
if (mounted) ToastUtils.showError('请失败: $e');
} finally {
if (mounted) setState(() => _isSubmitting = false);
}
}
// ============================================
// 建 UI
// 建 UI
// ============================================
@override
Widget build(BuildContext context) {
final colorScheme = context.colors;
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colorScheme.background,
backgroundColor: colorScheme.surface,
appBar: AppBar(
leading: IconButton(
icon: const Icon(LucideIcons.arrowLeft, size: 20),
icon: Icon(LucideIcons.arrowLeft,
color: colorScheme.onSurface, size: 20),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
'',
'',
style: AppTextStyles.headlineLarge(context).copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
backgroundColor: colorScheme.background,
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(AppSpacing.lg),
padding: const EdgeInsets.fromLTRB(
AppSpacing.md, AppSpacing.xs, AppSpacing.md, AppSpacing.xl),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 餘額顯示
// 可用余额
if (widget.balance != null) ...[
Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'可用餘額',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
Text(
'${widget.balance} USDT',
style: AppTextStyles.headlineMedium(context).copyWith(
fontWeight: FontWeight.bold,
color: context.appColors.up,
),
),
],
),
),
_buildBalanceRow(colorScheme),
const SizedBox(height: AppSpacing.lg),
],
// 表單
ShadForm(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 提現金額
Text(
'提現金額',
style: AppTextStyles.bodyLarge(context).copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: AppSpacing.sm),
ShadInputFormField(
id: 'amount',
controller: _amountController,
placeholder: const Text('請輸入提現金額 (USDT)'),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
validator: Validators.amount,
),
const SizedBox(height: AppSpacing.sm),
// 提现金额
_buildSectionLabel(colorScheme, '提现金额'),
const SizedBox(height: AppSpacing.sm),
_buildAmountInput(colorScheme),
const SizedBox(height: AppSpacing.sm),
// 手續費提示
ValueListenableBuilder<String>(
valueListenable: _feeNotifier,
builder: (_, feeText, __) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.info_outline,
size: 14, color: colorScheme.onSurfaceVariant),
const SizedBox(width: AppSpacing.xs),
Expanded(
child: Text(
feeText,
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
);
},
),
const SizedBox(height: AppSpacing.lg),
// 手续费提示
_buildFeeTip(colorScheme),
const SizedBox(height: AppSpacing.lg),
// 提現網絡
ValueListenableBuilder<List<String>>(
valueListenable: _networksNotifier,
builder: (_, networks, __) {
if (networks.isEmpty) return const SizedBox.shrink();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'提現網絡',
style: AppTextStyles.bodyLarge(context).copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: AppSpacing.sm),
ValueListenableBuilder<String?>(
valueListenable: _selectedNetworkNotifier,
builder: (_, selected, __) {
return SizedBox(
width: double.infinity,
child: ShadSelect<String>(
placeholder: const Text('選擇提現網絡'),
initialValue: selected,
selectedOptionBuilder: (context, val) =>
Text(val),
onChanged: (value) {
if (value != null) {
_selectedNetworkNotifier.value = value;
}
},
options: networks
.map((n) =>
ShadOption(value: n, child: Text(n)))
.toList(),
),
);
},
),
const SizedBox(height: AppSpacing.lg),
],
);
},
),
// 提现网络
if (_networks.isNotEmpty) ...[
_buildSectionLabel(colorScheme, '提现网络'),
const SizedBox(height: AppSpacing.sm),
_buildNetworkSelector(colorScheme),
const SizedBox(height: AppSpacing.lg),
],
// 目地址
Text(
'目標地址',
style: AppTextStyles.bodyLarge(context).copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: AppSpacing.sm),
ShadInputFormField(
id: 'address',
controller: _addressController,
placeholder: const Text('請輸入提現地址'),
validator: (v) => Validators.required(v, '提現地址'),
),
const SizedBox(height: AppSpacing.lg),
// 目地址
_buildSectionLabel(colorScheme, '目标地址'),
const SizedBox(height: AppSpacing.sm),
_buildAddressInput(colorScheme),
const SizedBox(height: AppSpacing.lg),
// 聯繫方式
Text(
'聯繫方式(可選)',
style: AppTextStyles.bodyLarge(context).copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: AppSpacing.sm),
ShadInputFormField(
id: 'contact',
controller: _contactController,
placeholder: const Text('方便客服與您聯繫'),
),
],
),
),
// 联系方式
_buildSectionLabel(colorScheme, '联系方式(可选)'),
const SizedBox(height: AppSpacing.sm),
_buildContactInput(colorScheme),
const SizedBox(height: AppSpacing.xl),
// 安全提示
Container(
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColorScheme.warning.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: AppColorScheme.warning.withValues(alpha: 0.15),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.shield_outlined,
size: 16, color: AppColorScheme.warning),
const SizedBox(width: AppSpacing.xs),
Text(
'安全提示',
style: AppTextStyles.labelLarge(context).copyWith(
fontWeight: FontWeight.w600,
color: AppColorScheme.warning,
),
),
],
),
const SizedBox(height: AppSpacing.sm),
Text(
'• 請仔細核對提現地址,地址錯誤將導致資產無法找回\n'
'• 提現申請提交後需等待管理員審批\n'
'• 提現將扣除 10% 手續費',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.6,
),
),
],
),
),
_buildSecurityTips(colorScheme),
const SizedBox(height: AppSpacing.xl),
// 提交按
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: _isSubmitting ? null : _submitWithdraw,
style: ElevatedButton.styleFrom(
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
disabledBackgroundColor:
colorScheme.primary.withValues(alpha: 0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.lg),
),
elevation: 0,
),
child: _isSubmitting
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: Text(
'提交申請',
style: AppTextStyles.headlineMedium(context).copyWith(
fontWeight: FontWeight.w600,
color: colorScheme.onPrimary,
),
),
),
),
const SizedBox(height: AppSpacing.lg),
// 提交按
_buildSubmitButton(colorScheme),
const SizedBox(height: AppSpacing.md),
// 底部安全保障
Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.verified_user_outlined,
size: 14,
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
),
const SizedBox(width: AppSpacing.xs),
Text(
'資金安全由平台全程保障',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
),
),
],
),
),
_buildBottomTip(colorScheme),
],
),
),
);
}
// ============================================
// 组件
// ============================================
Widget _buildBalanceRow(ColorScheme colorScheme) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'可用余额',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
Text(
'${widget.balance} USDT',
style: AppTextStyles.bodyMedium(context).copyWith(
fontWeight: FontWeight.w600,
),
),
],
),
);
}
Widget _buildSectionLabel(ColorScheme colorScheme, String text) {
return Text(
text,
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
);
}
Widget _buildAmountInput(ColorScheme colorScheme) {
return Container(
width: double.infinity,
height: 52,
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: _amountFocus.hasFocus
? colorScheme.secondary
: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: TextField(
controller: _amountController,
focusNode: _amountFocus,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,8}')),
],
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
decoration: InputDecoration(
hintText: '0.00',
hintStyle: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color:
colorScheme.onSurfaceVariant.withValues(alpha: 0.3),
),
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
),
),
Text(
'USDT',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
Widget _buildFeeTip(ColorScheme colorScheme) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(LucideIcons.info,
size: 13, color: colorScheme.onSurfaceVariant),
const SizedBox(width: 6),
Expanded(
child: Text(
_feeText,
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
);
}
Widget _buildNetworkSelector(ColorScheme colorScheme) {
return Container(
width: double.infinity,
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _selectedNetwork,
isExpanded: true,
hint: Text(
'选择提现网络',
style: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
items: _networks
.map((n) => DropdownMenuItem(
value: n,
child: Text(n),
))
.toList(),
onChanged: (value) {
if (value != null) setState(() => _selectedNetwork = value);
},
),
),
);
}
Widget _buildAddressInput(ColorScheme colorScheme) {
return Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.center,
child: TextField(
controller: _addressController,
style: AppTextStyles.bodyMedium(context),
decoration: InputDecoration(
hintText: '请输入提现地址',
hintStyle: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
),
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
),
);
}
Widget _buildContactInput(ColorScheme colorScheme) {
return Container(
width: double.infinity,
height: 48,
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
width: 1,
),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.center,
child: TextField(
controller: _contactController,
style: AppTextStyles.bodyMedium(context),
decoration: InputDecoration(
hintText: '方便客服与您联系',
hintStyle: AppTextStyles.bodyMedium(context).copyWith(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
),
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
),
);
}
Widget _buildSecurityTips(ColorScheme colorScheme) {
final tips = [
'请仔细核对提现地址,地址错误将导致资产无法找回',
'提现申请提交后需等待管理员审批',
'提现将扣除 10% 手续费',
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: tips
.map((t) => Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
Expanded(
child: Text(
t,
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
),
],
),
))
.toList(),
);
}
Widget _buildSubmitButton(ColorScheme colorScheme) {
return SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isSubmitting ? null : _submitWithdraw,
style: ElevatedButton.styleFrom(
backgroundColor: colorScheme.onSurface,
foregroundColor: colorScheme.surface,
disabledBackgroundColor: colorScheme.surfaceContainerHighest,
disabledForegroundColor: colorScheme.onSurfaceVariant,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
child: _isSubmitting
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
colorScheme.onSurfaceVariant,
),
),
)
: Text(
'提交申请',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
);
}
Widget _buildBottomTip(ColorScheme colorScheme) {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(LucideIcons.shieldCheck,
size: 13, color: colorScheme.onSurfaceVariant),
const SizedBox(width: 4),
Text(
'资金安全由平台全程保障',
style: AppTextStyles.bodySmall(context).copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
}