diff --git a/docs/features/apply-new-styles.md b/docs/features/apply-new-styles.md deleted file mode 100644 index 900e1d6..0000000 --- a/docs/features/apply-new-styles.md +++ /dev/null @@ -1,218 +0,0 @@ -# 应用新设计系统功能规格 - -## 1. 功能概述 -- **功能名称**: 应用新设计系统到 Flutter 项目 -- **优先级**: P0 -- **负责人**: 开发团队 -- **预计工期**: 1 天 - -## 2. 业务背景 - -### 当前痛点 -- 现有 Flutter 项目使用旧的样式系统 -- 缺乏统一的组件化设计 -- 视觉效果不够现代化 -- 缺少毛玻璃、霓虹光效等现代 UI 效果 - -### 解决方案 -- 应用新的 Material Design 3 配色方案 -- 使用 Space Grotesk + Manrope 字体组合 -- 组件化开发,提取可复用组件 -- 添加 Glass Panel 和 Neon Glow 效果 - -### 预期收益 -- 统一的视觉风格 -- 更好的用户体验 -- 提高开发效率(组件复用) -- 现代化的 UI 效果 - -## 3. 功能详情 - -### 3.1 用户故事 -作为 Monisuo 用户,我希望应用有现代化的 UI 设计,以便获得更好的使用体验和视觉享受。 - -### 3.2 功能列表 -- [ ] 更新颜色系统(Material Design 3) -- [ ] 更新字体系统(Space Grotesk + Manrope) -- [ ] 创建 GlassPanel 组件 -- [ ] 创建 NeonGlow 效果组件 -- [ ] 更新交易页面样式 -- [ ] 更新行情页面样式 -- [ ] 更新我的页面样式 -- [ ] 创建充值弹窗组件 -- [ ] 创建提现弹窗组件 - -### 3.3 UI/UX 设计 - -#### 配色方案 -```dart -// Primary Colors -primary: #72dcff (青色) -secondary: #dd8bfb (紫色) -tertiary: #afffd1 (绿色) - -// Surface Colors -background: #0b0e14 -surface: #0b0e14 -surfaceContainer: #161a21 -surfaceContainerHigh: #1c2028 -surfaceContainerHighest: #22262f - -// Semantic Colors -error: #ff716c -success: #afffd1 -warning: #ffa8a3 -``` - -#### 字体系统 -```dart -// Fonts -headline: Space Grotesk (标题) -body: Manrope (正文) -label: Manrope (标签) - -// Icons -Material Symbols Outlined -``` - -#### 组件风格 -- **Glass Panel**: 毛玻璃效果(backdrop-filter: blur(20px)) -- **Neon Glow**: 霓虹光效(box-shadow: 0 0 15px rgba(114, 220, 255, 0.3)) -- **Dark Theme**: 深色主题为主 -- **Rounded Corners**: 大圆角设计(2rem, 1rem) - -## 4. 技术方案 - -### 4.1 架构设计 - -#### 前端方案 (Flutter) -- **状态管理**: Provider (保持现有) -- **UI 组件**: shadcn_ui (保持现有) -- **设计系统**: 新增 AppDesignSystem - -#### 新增组件 -``` -lib/ui/components/ -├── glass_panel.dart # 毛玻璃面板 -├── neon_button.dart # 霓虹按钮 -├── neon_card.dart # 霓虹卡片 -└── modern_dialog.dart # 现代弹窗(已存在,需更新) -``` - -#### 更新文件 -``` -lib/core/theme/ -├── app_color_scheme.dart # 更新颜色系统 -├── app_text_styles.dart # 更新字体系统 -└── app_design_system.dart # 新增设计系统 - -lib/ui/pages/ -├── trade/trade_page.dart # 更新交易页面 -├── market/market_page.dart # 更新行情页面 -└── mine/mine_page.dart # 更新我的页面 -``` - -### 4.2 API 设计 - -**保持现有 API 不变**,只更新 UI 层。 - -### 4.3 数据模型 - -**保持现有数据模型不变**。 - -## 5. 测试用例 - -### 5.1 正常流程测试 -- [ ] 所有页面正常显示 -- [ ] Glass Panel 效果正常 -- [ ] Neon Glow 效果正常 -- [ ] 字体正确显示 -- [ ] 颜色正确应用 - -### 5.2 兼容性测试 -- [ ] 现有 API 调用正常 -- [ ] 现有功能正常工作 -- [ ] 主题切换正常 -- [ ] 明暗模式正常 - -### 5.3 边界条件测试 -- [ ] 小屏幕设备显示正常 -- [ ] 大屏幕设备显示正常 -- [ ] 横屏显示正常 -- [ ] 性能测试通过 - -## 6. 验收标准 - -### 功能验收 -- [x] 所有功能点已实现 -- [ ] UI 符合设计稿 -- [ ] 交互流畅自然 - -### 质量验收 -- [ ] flutter analyze 无错误 -- [ ] 现有测试通过 -- [ ] 代码审查通过 -- [ ] 无明显性能问题 - -### 文档验收 -- [ ] 设计系统文档已更新 -- [ ] 组件使用说明已添加 -- [ ] CHANGELOG 已更新 - -## 7. 依赖与风险 - -### 依赖项 -- ✅ Google Fonts 包已安装 -- ✅ shadcn_ui 已集成 -- ✅ Provider 状态管理已使用 - -### 潜在风险 -- ⚠️ 字体加载可能影响性能 → 使用缓存策略 -- ⚠️ 毛玻璃效果在低端设备可能卡顿 → 添加性能检测 -- ⚠️ 新样式可能与现有样式冲突 → 分步骤更新 - -## 8. 时间规划 - -| 阶段 | 任务 | 预计时间 | -|------|------|----------| -| Phase 1 | 创建设计系统和基础组件 | 2 小时 | -| Phase 2 | 更新页面样式 | 3 小时 | -| Phase 3 | 测试和优化 | 1 小时 | -| Phase 4 | 构建和部署 | 0.5 小时 | - -## 9. 开发约束 - -### ⚠️ 重要约束 -1. **不破坏现有 API 接口** -2. **保持现有业务逻辑** -3. **兼容现有数据模型** -4. **渐进式更新,避免大规模重构** - -### ✅ 允许的操作 -- 更新 UI 样式 -- 创建新的 UI 组件 -- 更新主题系统 -- 优化视觉效果 - -### ❌ 禁止的操作 -- 修改 API 接口定义 -- 修改数据模型结构 -- 修改业务逻辑代码 -- 删除现有功能 - -## 10. 参考资料 - -- 交易页面样式: `页面样式/交易.txt` -- 行情页面样式: `页面样式/行情.txt` -- 我的页面样式: `页面样式/我的.txt` -- 充值弹窗样式: `页面样式/充值弹窗.txt` -- 提现弹窗样式: `页面样式/提现弹窗.txt` -- Material Design 3: https://m3.material.io/ -- Space Grotesk Font: https://fonts.google.com/specimen/Space+Grotesk -- Manrope Font: https://fonts.google.com/specimen/Manrope - ---- - -**创建日期**: 2026-03-24 01:42 GMT+8 -**最后更新**: 2026-03-24 01:42 GMT+8 -**状态**: ✅ 准备就绪 diff --git a/docs/features/bottom-nav-labels.md b/docs/features/bottom-nav-labels.md deleted file mode 100644 index 0b9eaad..0000000 --- a/docs/features/bottom-nav-labels.md +++ /dev/null @@ -1,88 +0,0 @@ -# 底部导航栏优化 - 添加图标下方文字 - -## 1. 功能概述 -- **功能名称**: 底部导航栏添加文字标签 -- **优先级**: P1 -- **负责人**: 开发团队 -- **预计工期**: 30 分钟 - -## 2. 业务背景 - -### 当前痛点 -- 底部导航栏只有图标,没有文字 -- 用户需要猜测每个图标的功能 -- 用户体验不够友好 - -### 解决方案 -- 在图标下方添加文字标签 -- 提高可识别性和易用性 - -### 预期收益 -- ✅ 提升用户体验 -- ✅ 提高导航清晰度 -- ✅ 符合 Material Design 规范 - -## 3. UI/UX 设计 - -### 当前样式 -``` -[🏠] [📈] [💱] [💰] [👤] -``` - -### 修改后样式 -``` -[🏠] [📈] [💱] [💰] [👤] -首页 行情 交易 资产 我的 -``` - -## 4. 技术方案 - -### 修改文件 -- `lib/ui/pages/main/main_page.dart` - -### 修改内容 - -**当前代码**: -```dart -Icon( - item.icon, - size: 24, - color: isActive ? AppColorScheme.darkPrimary : AppColorScheme.darkOnSurfaceVariant, -) -``` - -**修改后代码**: -```dart -Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - item.icon, - size: 24, - color: isActive ? colorScheme.primary : colorScheme.onSurfaceVariant, - ), - const SizedBox(height: 4), - Text( - item.label, - style: TextStyle( - fontSize: 12, - color: isActive ? colorScheme.primary : colorScheme.onSurfaceVariant, - fontWeight: isActive ? FontWeight.w600 : FontWeight.normal, - ), - ), - ], -) -``` - -## 5. 验收标准 - -- [ ] 每个图标下方都有文字标签 -- [ ] 文字清晰可读 -- [ ] 激活状态文字加粗 -- [ ] 支持主题切换 -- [ ] 不影响现有功能 - ---- - -**创建日期**: 2026-03-24 02:50 GMT+8 -**状态**: 待执行 diff --git a/docs/features/theme-dynamic-colors.md b/docs/features/theme-dynamic-colors.md deleted file mode 100644 index e1cb245..0000000 --- a/docs/features/theme-dynamic-colors.md +++ /dev/null @@ -1,93 +0,0 @@ -# 修复主题切换功能 - 动态颜色系统 - -## 1. 功能概述 -- **功能名称**: 修复主题切换,支持明暗主题动态切换 -- **优先级**: P0 -- **负责人**: 开发团队 -- **预计工期**: 2 小时 - -## 2. 业务背景 - -### 当前痛点 -- 所有页面使用硬编码的 `AppColorScheme.dark*` 颜色 -- 主题切换功能不生效 -- 只有深色主题,没有浅色主题 -- 用户体验不一致 - -### 解决方案 -- 使用 `Theme.of(context)` 动态获取当前主题颜色 -- 所有页面支持明暗主题切换 -- 统一使用 Theme Provider 提供的主题 - -### 预期收益 -- ✅ 主题切换功能正常工作 -- ✅ 支持深色和浅色主题 -- ✅ 提升用户体验 -- ✅ 代码更规范 - -## 3. 技术方案 - -### 3.1 问题代码示例 - -**❌ 错误写法**(当前): -```dart -Container( - color: AppColorScheme.darkBackground, // 硬编码深色 - child: Text( - 'Hello', - style: TextStyle(color: AppColorScheme.darkOnSurface), // 硬编码 - ), -) -``` - -**✅ 正确写法**(修复后): -```dart -Container( - color: Theme.of(context).colorScheme.background, // 动态 - child: Text( - 'Hello', - style: TextStyle(color: Theme.of(context).colorScheme.onSurface), // 动态 - ), -) -``` - -### 3.2 修改策略 - -1. **替换所有硬编码颜色** - - `AppColorScheme.darkBackground` → `Theme.of(context).colorScheme.background` - - `AppColorScheme.darkOnSurface` → `Theme.of(context).colorScheme.onSurface` - - 等等... - -2. **确保 ThemeProvider 正常工作** - - 检查 ThemeProvider 实现 - - 确保明暗主题配置正确 - -3. **测试主题切换** - - 在"我的"页面测试主题切换 - - 验证所有页面响应主题变化 - -## 4. 需要修改的文件 - -### 主要页面 -1. `lib/ui/pages/home/home_page.dart` - 首页 -2. `lib/ui/pages/market/market_page.dart` - 行情页面 -3. `lib/ui/pages/trade/trade_page.dart` - 交易页面 -4. `lib/ui/pages/asset/asset_page.dart` - 资产页面 -5. `lib/ui/pages/mine/mine_page.dart` - 我的页面 -6. `lib/ui/pages/orders/*.dart` - 订单页面 - -### 组件 -7. `lib/ui/components/*.dart` - 所有组件 - -## 5. 验收标准 - -- [ ] 所有页面使用动态颜色(Theme.of(context)) -- [ ] 主题切换功能正常工作 -- [ ] 深色主题显示正常 -- [ ] 浅色主题显示正常 -- [ ] 所有页面响应主题变化 -- [ ] flutter analyze 0 errors - ---- - -**创建日期**: 2026-03-24 02:34 GMT+8 diff --git a/flutter_monisuo/lib/ui/pages/asset/deposit_page.dart b/flutter_monisuo/lib/ui/pages/asset/deposit_page.dart index 47ead00..d84610a 100644 --- a/flutter_monisuo/lib/ui/pages/asset/deposit_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/deposit_page.dart @@ -2,15 +2,12 @@ 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'; -/// 充值頁面 — 獨立頁面,替代彈窗 +/// 充值页面 class DepositPage extends StatefulWidget { const DepositPage({super.key}); @@ -20,24 +17,44 @@ class DepositPage extends StatefulWidget { class _DepositPageState extends State { final _amountController = TextEditingController(); - final _formKey = GlobalKey(); + final _amountFocus = FocusNode(); bool _isSubmitting = false; - // 充值結果狀態 + // 充值结果 bool _showResult = false; String? _orderNo; String? _amount; String? _walletAddress; String _walletNetwork = 'TRC20'; + @override + void initState() { + super.initState(); + _amountController.addListener(() => setState(() {})); + } + @override void dispose() { _amountController.dispose(); + _amountFocus.dispose(); super.dispose(); } + // ============================================ + // 业务逻辑 + // ============================================ + Future _submitDeposit() async { - if (!_formKey.currentState!.saveAndValidate()) return; + final text = _amountController.text; + final n = double.tryParse(text); + if (text.isEmpty || n == null || n <= 0) { + ToastUtils.showError('请输入有效金额'); + return; + } + if (n < 1000) { + ToastUtils.showError('单笔最低充值 1000 USDT'); + return; + } if (_isSubmitting) return; setState(() => _isSubmitting = true); @@ -52,14 +69,16 @@ class _DepositPageState extends State { _showResult = true; _orderNo = response.data!['orderNo'] as String? ?? ''; _amount = response.data!['amount']?.toString() ?? '0.00'; - _walletAddress = response.data!['walletAddress'] as String? ?? ''; - _walletNetwork = response.data!['walletNetwork'] as String? ?? 'TRC20'; + _walletAddress = + response.data!['walletAddress'] as String? ?? ''; + _walletNetwork = + response.data!['walletNetwork'] as String? ?? 'TRC20'; }); } 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); } @@ -71,8 +90,8 @@ class _DepositPageState extends State { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('確認已打款'), - content: const Text('確認您已完成向指定地址的轉賬?'), + title: const Text('确认已打款'), + content: const Text('确认您已完成向指定地址的转账?'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), @@ -80,7 +99,7 @@ class _DepositPageState extends State { ), TextButton( onPressed: () => Navigator.pop(ctx, true), - child: const Text('確認'), + child: const Text('确认'), ), ], ), @@ -89,42 +108,44 @@ class _DepositPageState extends State { if (confirmed != true || !mounted) return; try { - final response = await context.read().confirmPay(_orderNo!); + final response = + await context.read().confirmPay(_orderNo!); 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'); } } // ============================================ - // 構建 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, @@ -133,183 +154,221 @@ class _DepositPageState extends State { ); } - /// 表單視圖 — 輸入充值金額 + // ============================================ + // 表单视图 + // ============================================ + Widget _buildFormView() { - final colorScheme = context.colors; + final colorScheme = Theme.of(context).colorScheme; return 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: [ - // 幣種選擇卡片 - 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( - children: [ - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: colorScheme.primary.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(AppRadius.md), - ), - child: Icon( - LucideIcons.coins, - color: colorScheme.primary, - size: 20, - ), - ), - const SizedBox(width: AppSpacing.md), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'USDT', - style: AppTextStyles.headlineMedium(context).copyWith( - fontWeight: FontWeight.bold, - ), - ), - Text( - 'Tether USD', - style: AppTextStyles.bodySmall(context).copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ), - ), - const SizedBox(height: AppSpacing.xl), - - // 金額輸入 - Text( - '充值金額', - style: AppTextStyles.bodyLarge(context).copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: AppSpacing.sm), - ShadForm( - key: _formKey, - child: ShadInputFormField( - id: 'amount', - controller: _amountController, - placeholder: const Text('請輸入充值金額'), - keyboardType: const TextInputType.numberWithOptions(decimal: true), - validator: (v) { - if (v == null || v.isEmpty) return '請輸入金額'; - final n = double.tryParse(v); - if (n == null || n <= 0) return '請輸入有效金額'; - if (n < 1000) return '單筆最低充值 1000 USDT'; - return null; - }, - ), - ), - const SizedBox(height: AppSpacing.sm), - - // 最低金額提示 - Container( - padding: const EdgeInsets.symmetric( - horizontal: AppSpacing.md, - vertical: AppSpacing.sm, - ), - decoration: BoxDecoration( - color: AppColorScheme.info.withValues(alpha: 0.08), - borderRadius: BorderRadius.circular(AppRadius.md), - border: Border.all( - color: AppColorScheme.info.withValues(alpha: 0.15), - ), - ), - child: Row( - children: [ - Icon(Icons.info_outline, size: 16, color: AppColorScheme.info), - const SizedBox(width: AppSpacing.sm), - Text( - '單筆最低充值 1000 USDT', - style: AppTextStyles.bodySmall(context).copyWith( - color: AppColorScheme.info, - ), - ), - ], - ), - ), - const SizedBox(height: AppSpacing.xl), - - // 操作說明 - Text( - '操作流程', - style: AppTextStyles.bodyLarge(context).copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: AppSpacing.md), - _buildStepItem(1, '輸入充值金額並提交申請'), - _buildStepItem(2, '向指定錢包地址轉賬'), - _buildStepItem(3, '點擊「已打款」確認'), - _buildStepItem(4, '等待管理員審核通過'), - - const SizedBox(height: AppSpacing.xxl), - - // 提交按鈕 - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - onPressed: _isSubmitting ? null : _submitDeposit, - 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, - ), - ), - ), - ), + // 币种行 + _buildCoinRow(colorScheme), const SizedBox(height: AppSpacing.lg), - // 安全提示 - _buildSecurityTip(), + // 金额输入 + _buildAmountLabel(colorScheme), + const SizedBox(height: AppSpacing.sm), + _buildAmountInput(colorScheme), + const SizedBox(height: AppSpacing.sm), + + // 最低金额提示 + _buildMinTip(colorScheme), + const SizedBox(height: AppSpacing.xl), + + // 操作流程 + _buildSteps(colorScheme), + const SizedBox(height: AppSpacing.xxl), + + // 提交按钮 + _buildSubmitButton(colorScheme, '下一步', _submitDeposit), + const SizedBox(height: AppSpacing.md), + + // 底部提示 + _buildBottomTip(colorScheme), ], ), ); } - /// 結果視圖 — 展示錢包地址和確認打款 + Widget _buildCoinRow(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( + children: [ + Text( + 'USDT', + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(width: 8), + Text( + 'Tether USD', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ); + } + + Widget _buildAmountLabel(ColorScheme colorScheme) { + return 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 _buildMinTip(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), + Text( + '单笔最低充值 1000 USDT', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ); + } + + Widget _buildSteps(ColorScheme colorScheme) { + final steps = [ + '输入充值金额并提交申请', + '向指定钱包地址转账', + '点击「已打款」确认', + '等待管理员审核通过', + ]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '操作流程', + style: AppTextStyles.bodyMedium(context).copyWith( + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 12), + ...steps.asMap().entries.map((e) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + children: [ + Text( + '${e.key + 1}.', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + e.value, + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + )), + ], + ); + } + + // ============================================ + // 结果视图 + // ============================================ + Widget _buildResultView() { - final colorScheme = context.colors; + final colorScheme = Theme.of(context).colorScheme; return 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: [ @@ -317,29 +376,21 @@ class _DepositPageState extends State { Center( child: Column( children: [ - Container( - width: 56, - height: 56, - decoration: BoxDecoration( - color: context.appColors.up.withValues(alpha: 0.12), - shape: BoxShape.circle, - ), - child: Icon( - Icons.check_circle_outline_rounded, - color: context.appColors.up, - size: 32, + Icon( + LucideIcons.circleCheck, + size: 48, + color: colorScheme.secondary, + ), + const SizedBox(height: 12), + Text( + '充值申请成功', + style: AppTextStyles.headlineSmall(context).copyWith( + fontWeight: FontWeight.w600, ), ), - const SizedBox(height: AppSpacing.md), + const SizedBox(height: 4), Text( - '充值申請成功', - style: AppTextStyles.headlineLarge(context).copyWith( - fontWeight: FontWeight.w700, - ), - ), - const SizedBox(height: AppSpacing.xs), - Text( - '請向以下地址轉賬完成充值', + '请向以下地址转账完成充值', style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, ), @@ -349,113 +400,28 @@ class _DepositPageState extends State { ), const SizedBox(height: AppSpacing.xl), - // 訂單信息 - 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: Column( - children: [ - _buildInfoRow('訂單號', _orderNo ?? ''), - const SizedBox(height: AppSpacing.sm), - Divider(color: colorScheme.outlineVariant.withValues(alpha: 0.2), height: 1), - const SizedBox(height: AppSpacing.sm), - _buildInfoRow('充值金額', '${_amount ?? "0.00"} USDT', isBold: true), - ], - ), - ), + // 订单信息 + _buildInfoCard(colorScheme), const SizedBox(height: AppSpacing.lg), - // 錢包地址 - Text( - '收款地址', - style: AppTextStyles.bodyLarge(context).copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: AppSpacing.sm), - 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: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text( - _walletAddress ?? '', - style: AppTextStyles.bodyMedium(context).copyWith( - fontFamily: 'monospace', - ), - ), - ), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: _walletAddress ?? '')); - ToastUtils.showSuccess('地址已複製到剪貼板'); - }, - child: Container( - padding: const EdgeInsets.all(AppSpacing.sm), - decoration: BoxDecoration( - color: colorScheme.primary.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(AppRadius.sm), - ), - child: Icon( - LucideIcons.copy, - size: 16, - color: colorScheme.primary, - ), - ), - ), - ], - ), - const SizedBox(height: AppSpacing.sm), - Text( - '網絡: $_walletNetwork', - style: AppTextStyles.bodySmall(context).copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), + // 收款地址 + _buildAddressCard(colorScheme), const SizedBox(height: AppSpacing.md), - // 重要提示 - 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), - ), - ), + // 提示 + Padding( + padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon(Icons.info_outline, size: 18, color: AppColorScheme.warning), - const SizedBox(width: AppSpacing.sm), + Icon(LucideIcons.info, + size: 13, color: colorScheme.onSurfaceVariant), + const SizedBox(width: 6), Expanded( child: Text( - '轉賬完成後請點擊下方「已打款」按鈕確認,管理員審核通過後資金將到賬。', - style: AppTextStyles.bodyMedium(context).copyWith( - color: AppColorScheme.warning, + '转账完成后请点击「已打款」确认,管理员审核通过后资金将到账。', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, ), ), ), @@ -464,24 +430,24 @@ class _DepositPageState extends State { ), const SizedBox(height: AppSpacing.xl), - // 操作按鈕 + // 操作按钮 Row( children: [ Expanded( child: SizedBox( - height: 50, + height: 48, child: OutlinedButton( onPressed: () => Navigator.of(context).pop(), style: OutlinedButton.styleFrom( side: BorderSide(color: colorScheme.outline), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppRadius.lg), + borderRadius: BorderRadius.circular(14), ), ), child: Text( - '稍後確認', - style: AppTextStyles.headlineSmall(context).copyWith( - fontWeight: FontWeight.w600, + '稍后确认', + style: AppTextStyles.bodyMedium(context).copyWith( + fontWeight: FontWeight.w500, ), ), ), @@ -490,22 +456,22 @@ class _DepositPageState extends State { const SizedBox(width: AppSpacing.md), Expanded( child: SizedBox( - height: 50, + height: 48, child: ElevatedButton( onPressed: _confirmPay, style: ElevatedButton.styleFrom( - backgroundColor: colorScheme.primary, - foregroundColor: colorScheme.onPrimary, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppRadius.lg), - ), + backgroundColor: colorScheme.onSurface, + foregroundColor: colorScheme.surface, elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), ), child: Text( '已打款', - style: AppTextStyles.headlineSmall(context).copyWith( + style: AppTextStyles.bodyMedium(context).copyWith( fontWeight: FontWeight.w600, - color: colorScheme.onPrimary, + color: colorScheme.surface, ), ), ), @@ -513,100 +479,191 @@ class _DepositPageState extends State { ), ], ), - const SizedBox(height: AppSpacing.lg), - _buildSecurityTip(), ], ), ); } - // ============================================ - // 輔助組件 - // ============================================ - - Widget _buildStepItem(int step, String text) { - final colorScheme = context.colors; - return Padding( - padding: const EdgeInsets.only(bottom: AppSpacing.sm + AppSpacing.xs), - child: Row( + Widget _buildInfoCard(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: Column( children: [ - Container( - width: 24, - height: 24, - decoration: BoxDecoration( - color: colorScheme.primary.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(AppRadius.sm), - ), - child: Center( - child: Text( - '$step', - style: AppTextStyles.labelSmall(context).copyWith( - color: colorScheme.primary, - fontWeight: FontWeight.bold, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '订单号', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, ), ), - ), - ), - const SizedBox(width: AppSpacing.sm), - Expanded( - child: Text( - text, - style: AppTextStyles.bodyMedium(context).copyWith( - color: colorScheme.onSurfaceVariant, + Text( + _orderNo ?? '', + style: AppTextStyles.bodySmall(context).copyWith( + fontWeight: FontWeight.w500, + ), ), - ), + ], + ), + const SizedBox(height: 10), + Divider( + height: 1, + color: colorScheme.outlineVariant.withValues(alpha: 0.3)), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '充值金额', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + Text( + '${_amount ?? "0.00"} USDT', + style: AppTextStyles.bodyMedium(context).copyWith( + fontWeight: FontWeight.w600, + ), + ), + ], ), ], ), ); } - Widget _buildInfoRow(String label, String value, {bool isBold = false}) { - final colorScheme = context.colors; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + Widget _buildAddressCard(ColorScheme colorScheme) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - label, + '收款地址', style: AppTextStyles.bodyMedium(context).copyWith( - color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, ), ), - Flexible( - child: Text( - value, - style: AppTextStyles.bodyMedium(context).copyWith( - fontWeight: isBold ? FontWeight.bold : FontWeight.w400, + const SizedBox(height: AppSpacing.sm), + 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, ), - textAlign: TextAlign.right, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + _walletAddress ?? '', + style: AppTextStyles.bodyMedium(context).copyWith( + fontFamily: 'monospace', + ), + ), + ), + GestureDetector( + onTap: () { + Clipboard.setData( + ClipboardData(text: _walletAddress ?? '')); + ToastUtils.showSuccess('地址已复制'); + }, + child: Padding( + padding: const EdgeInsets.all(4), + child: Icon( + LucideIcons.copy, + size: 16, + color: colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + '网络: $_walletNetwork', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + ], ), ), ], ); } - Widget _buildSecurityTip() { - final colorScheme = context.colors; - return Container( + // ============================================ + // 共享组件 + // ============================================ + + Widget _buildSubmitButton( + ColorScheme colorScheme, String text, VoidCallback onPressed) { + return SizedBox( width: double.infinity, - padding: const EdgeInsets.all(AppSpacing.md), - decoration: BoxDecoration( - color: colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(AppRadius.md), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.verified_user_outlined, - size: 14, - color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), + height: 48, + child: ElevatedButton( + onPressed: _isSubmitting ? null : onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.onSurface, + foregroundColor: colorScheme.surface, + disabledBackgroundColor: + colorScheme.surfaceContainerHighest, + disabledForegroundColor: colorScheme.onSurfaceVariant, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), ), - const SizedBox(width: AppSpacing.xs), + ), + child: _isSubmitting + ? SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + colorScheme.onSurfaceVariant, + ), + ), + ) + : Text( + 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.withValues(alpha: 0.5), + color: colorScheme.onSurfaceVariant, ), ), ], diff --git a/flutter_monisuo/lib/ui/pages/asset/transfer_page.dart b/flutter_monisuo/lib/ui/pages/asset/transfer_page.dart index 34f1e3d..ab46b4b 100644 --- a/flutter_monisuo/lib/ui/pages/asset/transfer_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/transfer_page.dart @@ -1,15 +1,13 @@ -import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../../../core/theme/app_theme.dart'; -import '../../../core/theme/app_theme_extension.dart'; import '../../../core/theme/app_spacing.dart'; import '../../../providers/asset_provider.dart'; import '../../../data/models/account_models.dart'; -/// 劃轉頁面 - 現代化設計版本 +/// 划转页面 class TransferPage extends StatefulWidget { const TransferPage({super.key}); @@ -17,106 +15,38 @@ class TransferPage extends StatefulWidget { State createState() => _TransferPageState(); } -class _TransferPageState extends State - with TickerProviderStateMixin { +class _TransferPageState extends State { final _amountController = TextEditingController(); final _focusNode = FocusNode(); - int _direction = 1; // 1: 資金→交易, 2: 交易→資金 + int _direction = 1; // 1: 资金→交易, 2: 交易→资金 bool _isLoading = false; - // Animation controllers - late AnimationController _swapAnimationController; - late AnimationController _amountAnimationController; - late AnimationController _balanceAnimationController; - late Animation _swapAnimation; - late Animation _amountScaleAnimation; - - // Balance animation values - String _displayedFromBalance = '0.00'; - String _displayedToBalance = '0.00'; - String _targetFromBalance = '0.00'; - String _targetToBalance = '0.00'; - @override void initState() { super.initState(); - - // Initialize swap animation - _swapAnimationController = AnimationController( - duration: const Duration(milliseconds: 600), - vsync: this, - ); - _swapAnimation = CurvedAnimation( - parent: _swapAnimationController, - curve: Curves.easeInOutCubic, - ); - - // Initialize amount scale animation - _amountAnimationController = AnimationController( - duration: const Duration(milliseconds: 200), - vsync: this, - ); - _amountScaleAnimation = Tween(begin: 1.0, end: 1.05).animate( - CurvedAnimation( - parent: _amountAnimationController, - curve: Curves.easeInOut, - ), - ); - - // Initialize balance animation - _balanceAnimationController = AnimationController( - duration: const Duration(milliseconds: 500), - vsync: this, - )..addListener(_updateBalanceAnimation); - - _amountController.addListener(_onAmountChanged); - + _amountController.addListener(() => setState(() {})); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().refreshAll(force: true); - _updateBalanceTargets(); }); } @override void dispose() { - _amountController.removeListener(_onAmountChanged); _amountController.dispose(); _focusNode.dispose(); - _swapAnimationController.dispose(); - _amountAnimationController.dispose(); - _balanceAnimationController.removeListener(_updateBalanceAnimation); - _balanceAnimationController.dispose(); super.dispose(); } - void _onAmountChanged() { - // Trigger amount input animation - _amountAnimationController.forward().then((_) { - _amountAnimationController.reverse(); - }); - } - - void _updateBalanceTargets() { - setState(() { - _targetFromBalance = _fromBalance; - _targetToBalance = _toBalance; - _displayedFromBalance = _targetFromBalance; - _displayedToBalance = _targetToBalance; - }); - } - - void _updateBalanceAnimation() { - // This will be used for smooth balance transitions - } - // ============================================ - // 數據訪問 + // 数据访问 // ============================================ String get _fundBalance { try { final provider = context.read(); - final balance = provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00'; + final balance = provider.fundAccount?.balance ?? + provider.overview?.fundBalance ?? + '0.00'; return _formatBalance(balance); } catch (e) { return '0.00'; @@ -126,9 +56,7 @@ class _TransferPageState extends State String get _tradeUsdtBalance { try { final provider = context.read(); - if (provider.tradeAccounts.isEmpty) { - return '0.00'; - } + if (provider.tradeAccounts.isEmpty) return '0.00'; final usdtHolding = provider.tradeAccounts.firstWhere( (t) => t.coinCode.toUpperCase() == 'USDT', orElse: () => AccountTrade( @@ -149,18 +77,16 @@ class _TransferPageState extends State } } - String get _availableBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance; + String get _availableBalance => + _direction == 1 ? _fundBalance : _tradeUsdtBalance; - String get _fromLabel => _direction == 1 ? '資金賬戶' : '交易賬戶'; - String get _toLabel => _direction == 1 ? '交易賬戶' : '資金賬戶'; + String get _fromLabel => _direction == 1 ? '资金账户' : '交易账户'; + String get _toLabel => _direction == 1 ? '交易账户' : '资金账户'; String get _fromBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance; String get _toBalance => _direction == 1 ? _tradeUsdtBalance : _fundBalance; - IconData get _fromIcon => _direction == 1 ? LucideIcons.wallet : LucideIcons.trendingUp; - IconData get _toIcon => _direction == 1 ? LucideIcons.trendingUp : LucideIcons.wallet; - // ============================================ - // 業務邏輯 + // 业务逻辑 // ============================================ Future _doTransfer() async { @@ -169,12 +95,12 @@ class _TransferPageState extends State final transferAmount = double.tryParse(amount) ?? 0; if (transferAmount <= 0) { - _showSnackBar('請輸入有效的劃轉金額'); + _showSnackBar('请输入有效的划转金额'); return; } if (transferAmount > available) { - _showSnackBar('餘額不足'); + _showSnackBar('余额不足'); return; } @@ -182,26 +108,22 @@ class _TransferPageState extends State try { final response = await context.read().transfer( - direction: _direction, - amount: amount, - ); + direction: _direction, + amount: amount, + ); if (mounted) { if (response.success) { _amountController.clear(); - _showSnackBar('劃轉成功'); + _showSnackBar('划转成功'); await Future.delayed(const Duration(milliseconds: 500)); - if (mounted) { - Navigator.of(context).pop(true); - } + if (mounted) Navigator.of(context).pop(true); } else { - _showSnackBar(response.message ?? '劃轉失敗'); + _showSnackBar(response.message ?? '划转失败'); } } } finally { - if (mounted) { - setState(() => _isLoading = false); - } + if (mounted) setState(() => _isLoading = false); } } @@ -218,26 +140,19 @@ class _TransferPageState extends State void _setQuickAmount(double percent) { final available = double.tryParse(_availableBalance) ?? 0; final amount = available * percent; - _amountController.text = amount.toStringAsFixed(8).replaceAll(RegExp(r'\.?0+$'), ''); - - // Trigger haptic feedback + _amountController.text = amount + .toStringAsFixed(8) + .replaceAll(RegExp(r'\.?0+$'), ''); HapticFeedback.selectionClick(); } - Future _toggleDirection() async { - // Trigger haptic feedback + void _toggleDirection() { HapticFeedback.mediumImpact(); - - await _swapAnimationController.forward(); - setState(() { - _direction = _direction == 1 ? 2 : 1; - _updateBalanceTargets(); - }); - _swapAnimationController.reset(); + setState(() => _direction = _direction == 1 ? 2 : 1); } // ============================================ - // 構建 UI + // 构建 UI // ============================================ @override @@ -251,51 +166,34 @@ class _TransferPageState extends State elevation: 0, scrolledUnderElevation: 0, leading: IconButton( - icon: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(12), - ), - child: Icon(LucideIcons.arrowLeft, color: colorScheme.onSurface, size: 20), - ), + icon: Icon(LucideIcons.arrowLeft, + color: colorScheme.onSurface, size: 20), onPressed: () => Navigator.of(context).pop(), ), title: Text( - '賬戶劃轉', + '账户划转', style: AppTextStyles.headlineLarge(context).copyWith( - fontWeight: FontWeight.w700, + fontWeight: FontWeight.w600, ), ), centerTitle: true, ), body: Consumer( builder: (context, provider, _) { - return Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - colorScheme.surface, - colorScheme.surfaceContainerLowest.withValues(alpha: 0.3), - ], - ), - ), - child: SingleChildScrollView( - padding: const EdgeInsets.fromLTRB(AppSpacing.lg, AppSpacing.sm, AppSpacing.lg, AppSpacing.xl), - child: Column( - children: [ - _buildTransferDirectionCard(), - const SizedBox(height: AppSpacing.xl), - _buildAmountSection(), - const SizedBox(height: AppSpacing.xl), - _buildTipsCard(), - const SizedBox(height: AppSpacing.xl + AppSpacing.sm), - _buildConfirmButton(), - const SizedBox(height: AppSpacing.lg), - ], - ), + return SingleChildScrollView( + padding: const EdgeInsets.fromLTRB( + AppSpacing.md, AppSpacing.xs, AppSpacing.md, AppSpacing.xl), + child: Column( + children: [ + _buildTransferCard(colorScheme), + const SizedBox(height: AppSpacing.md), + _buildAmountSection(colorScheme), + const SizedBox(height: AppSpacing.md), + _buildTips(colorScheme), + const SizedBox(height: AppSpacing.xl), + _buildConfirmButton(colorScheme), + const SizedBox(height: AppSpacing.lg), + ], ), ); }, @@ -304,388 +202,275 @@ class _TransferPageState extends State } // ============================================ - // Transfer direction card - Modern Design + // 划转方向卡片 // ============================================ - Widget _buildTransferDirectionCard() { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - - // Gold gradient colors - final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B); - final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500); - + Widget _buildTransferCard(ColorScheme colorScheme) { return Container( width: double.infinity, - padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: isDark - ? [ - colorScheme.surfaceContainerHigh, - colorScheme.surfaceContainer, - ] - : [ - Colors.white, - colorScheme.surfaceContainerLow, - ], - ), - borderRadius: BorderRadius.circular(24), - boxShadow: [ - BoxShadow( - color: colorScheme.shadow.withValues(alpha: isDark ? 0.3 : 0.08), - blurRadius: 24, - offset: const Offset(0, 8), - ), - BoxShadow( - color: goldStart.withValues(alpha: 0.1), - blurRadius: 16, - offset: const Offset(0, 4), - spreadRadius: 2, - ), - ], + color: colorScheme.surface, + borderRadius: BorderRadius.circular(20), border: Border.all( - color: colorScheme.outlineVariant.withValues(alpha: 0.3), + color: colorScheme.outlineVariant.withValues(alpha: 0.5), width: 1, ), ), - child: Column( + child: Stack( + clipBehavior: Clip.none, children: [ - // From Account Card - _AnimatedAccountCard( - key: ValueKey('from-$_direction'), - label: '從', - accountName: _fromLabel, - balance: _displayedFromBalance, - icon: _fromIcon, - isSource: true, - animation: _swapAnimation, - ), - - // Swap Button - Enhanced Design - Padding( - padding: const EdgeInsets.symmetric(vertical: AppSpacing.md), - child: GestureDetector( - onTap: _toggleDirection, - child: Container( - width: 56, - height: 56, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [goldStart, goldEnd], - ), - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: goldStart.withValues(alpha: 0.4), - blurRadius: 16, - offset: const Offset(0, 4), + Column( + children: [ + // From + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + '从', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(width: 8), + Text( + _fromLabel, + style: + AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w500, + ), + ), + ], ), - BoxShadow( - color: goldStart.withValues(alpha: 0.2), - blurRadius: 8, - offset: const Offset(0, 2), + Text( + _fromBalance, + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w600, + ), ), ], ), - child: Container( - margin: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: colorScheme.surface, - shape: BoxShape.circle, - ), - child: Container( - margin: const EdgeInsets.all(2), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [goldStart, goldEnd], + ), + Divider( + height: 1, + thickness: 1, + color: colorScheme.outlineVariant.withValues(alpha: 0.5), + ), + // To + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + '到', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(width: 8), + Text( + _toLabel, + style: + AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w500, + ), + ), + ], + ), + Text( + _toBalance, + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w600, ), - shape: BoxShape.circle, - ), - child: Icon( - LucideIcons.arrowUpDown, - size: 24, - color: colorScheme.surface, ), + ], + ), + ), + ], + ), + // 交换按钮 - 右侧贴分割线 + Positioned( + right: 12, + top: 20, + child: GestureDetector( + onTap: _toggleDirection, + child: Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: colorScheme.surface, + shape: BoxShape.circle, + border: Border.all( + color: colorScheme.outlineVariant, + width: 1, ), ), + child: Icon( + LucideIcons.arrowUpDown, + size: 14, + color: colorScheme.onSurfaceVariant, + ), ), ), ), - - // To Account Card - _AnimatedAccountCard( - key: ValueKey('to-$_direction'), - label: '到', - accountName: _toLabel, - balance: _displayedToBalance, - icon: _toIcon, - isSource: false, - animation: _swapAnimation, - ), ], ), ); } // ============================================ - // Amount input section - Enhanced Design + // 金额输入区域 // ============================================ - Widget _buildAmountSection() { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - + Widget _buildAmountSection(ColorScheme colorScheme) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Header with label and max button + // 标题行 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Icon( - LucideIcons.coins, - size: 20, - color: colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Text( - '劃轉金額', - style: AppTextStyles.headlineSmall(context).copyWith( - color: colorScheme.onSurface, - fontWeight: FontWeight.w600, - ), - ), - ], + Text( + '划转金额', + style: AppTextStyles.bodyMedium(context).copyWith( + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), ), - Material( - color: Colors.transparent, - child: InkWell( - onTap: () => _setQuickAmount(1.0), - borderRadius: BorderRadius.circular(16), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - colorScheme.secondary.withValues(alpha: 0.15), - colorScheme.secondary.withValues(alpha: 0.05), - ], - ), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: colorScheme.secondary.withValues(alpha: 0.3), - width: 1, - ), - ), - child: Text( - '全部', - style: AppTextStyles.labelLarge(context).copyWith( - color: colorScheme.secondary, - fontWeight: FontWeight.w600, - ), - ), + GestureDetector( + onTap: () => _setQuickAmount(1.0), + child: Text( + '全部', + style: AppTextStyles.bodyMedium(context).copyWith( + color: colorScheme.secondary, + fontWeight: FontWeight.w500, ), ), ), ], ), - const SizedBox(height: 16), + const SizedBox(height: 12), - // Amount input card with glow effect - AnimatedBuilder( - animation: _amountScaleAnimation, - builder: (context, child) { - return Transform.scale( - scale: _amountScaleAnimation.value, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - boxShadow: _focusNode.hasFocus - ? [ - BoxShadow( - color: colorScheme.secondary.withValues(alpha: 0.2), - blurRadius: 16, - spreadRadius: 2, - ), - ] - : [], - ), - child: Container( - width: double.infinity, - height: 80, - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg, vertical: AppSpacing.md), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: isDark - ? [ - colorScheme.surfaceContainerHighest, - colorScheme.surfaceContainerHigh, - ] - : [ - Colors.white, - colorScheme.surfaceContainerLow, - ], - ), - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: _focusNode.hasFocus - ? colorScheme.secondary - : colorScheme.outlineVariant.withValues(alpha: 0.5), - width: _focusNode.hasFocus ? 2 : 1, - ), - boxShadow: [ - BoxShadow( - color: colorScheme.shadow.withValues(alpha: isDark ? 0.2 : 0.05), - blurRadius: 12, - offset: const Offset(0, 4), - ), - ], + // 金额输入框 + Container( + width: double.infinity, + height: 52, + decoration: BoxDecoration( + color: colorScheme.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: _focusNode.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: _focusNode, + 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, ), - child: Row( - children: [ - Expanded( - child: TextField( - controller: _amountController, - focusNode: _focusNode, - keyboardType: const TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,8}')), - ], - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w700, - color: colorScheme.onSurface, - letterSpacing: -0.5, - ), - decoration: InputDecoration( - hintText: '0.00', - hintStyle: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w700, - color: colorScheme.onSurfaceVariant.withValues(alpha: 0.3), - letterSpacing: -0.5, - ), - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - isDense: true, - ), - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(12), - ), - child: Text( - 'USDT', - style: AppTextStyles.headlineSmall(context).copyWith( - fontWeight: FontWeight.w600, - color: colorScheme.onSurfaceVariant, - ), - ), - ), - ], + 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, ), + onChanged: (_) => setState(() {}), ), ), - ); - }, - ), - const SizedBox(height: 16), - - // Available balance display - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.3), - borderRadius: BorderRadius.circular(12), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ Text( - '可用餘額', + 'USDT', style: AppTextStyles.bodyMedium(context).copyWith( color: colorScheme.onSurfaceVariant, - ), - ), - Text( - '${_formatBalance(_availableBalance)} USDT', - style: AppTextStyles.bodyLarge(context).copyWith( - fontWeight: FontWeight.w600, - color: colorScheme.onSurface, + fontWeight: FontWeight.w500, ), ), ], ), ), - const SizedBox(height: 20), + const SizedBox(height: 10), - // Percent buttons - Capsule design with gradient + // 可用余额 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '可用余额', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), + ), + Text( + '$_availableBalance USDT', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + const SizedBox(height: 12), + + // 百分比按钮 Row( children: [ - _buildPercentButton('25%', 0.25, 0), + _buildPercentButton('25%', 0.25, colorScheme), const SizedBox(width: 8), - _buildPercentButton('50%', 0.50, 1), + _buildPercentButton('50%', 0.50, colorScheme), const SizedBox(width: 8), - _buildPercentButton('75%', 0.75, 2), + _buildPercentButton('75%', 0.75, colorScheme), const SizedBox(width: 8), - _buildPercentButton('100%', 1.0, 3), + _buildPercentButton('100%', 1.0, colorScheme), ], ), ], ); } - Widget _buildPercentButton(String label, double percent, int index) { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B); - final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500); - + Widget _buildPercentButton( + String label, double percent, ColorScheme colorScheme) { return Expanded( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => _setQuickAmount(percent), - borderRadius: BorderRadius.circular(20), - child: Container( - height: 44, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - goldStart.withValues(alpha: 0.12), - goldEnd.withValues(alpha: 0.08), - ], - ), - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: goldStart.withValues(alpha: 0.25), - width: 1.5, - ), - ), - child: Center( - child: Text( - label, - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w600, - color: goldStart, - ), + child: GestureDetector( + onTap: () => _setQuickAmount(percent), + child: Container( + height: 34, + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + label, + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, ), ), ), @@ -695,64 +480,21 @@ class _TransferPageState extends State } // ============================================ - // Tips card - Refined Design + // 提示文字 // ============================================ - Widget _buildTipsCard() { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - final successColor = isDark ? const Color(0xFF4ADE80) : const Color(0xFF10B981); - - return Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - successColor.withValues(alpha: 0.08), - successColor.withValues(alpha: 0.03), - ], - ), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: successColor.withValues(alpha: 0.2), - width: 1, - ), - ), + Widget _buildTips(ColorScheme colorScheme) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: successColor.withValues(alpha: 0.15), - borderRadius: BorderRadius.circular(8), - ), - child: Icon( - LucideIcons.sparkles, - size: 18, - color: successColor, - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '即時到賬', - style: AppTextStyles.bodyMedium(context).copyWith( - color: successColor, - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 2), - Text( - '劃轉即時到賬,無需手續費', - style: AppTextStyles.bodySmall(context).copyWith( - color: successColor.withValues(alpha: 0.8), - ), - ), - ], + Icon(LucideIcons.info, + size: 13, color: colorScheme.onSurfaceVariant), + const SizedBox(width: 6), + Text( + '划转即时到账,无需手续费', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, ), ), ], @@ -761,88 +503,51 @@ class _TransferPageState extends State } // ============================================ - // Confirm button - Premium Design + // 确认按钮 // ============================================ - Widget _buildConfirmButton() { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - final goldStart = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B); - final goldEnd = isDark ? const Color(0xFFFFD700) : const Color(0xFFFFA500); - final hasAmount = _amountController.text.isNotEmpty && double.tryParse(_amountController.text) != null && double.parse(_amountController.text) > 0; + Widget _buildConfirmButton(ColorScheme colorScheme) { + final hasAmount = _amountController.text.isNotEmpty && + double.tryParse(_amountController.text) != null && + double.parse(_amountController.text) > 0; - return Container( - height: 60, - decoration: BoxDecoration( - gradient: hasAmount && !_isLoading - ? LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [goldStart, goldEnd], - ) - : null, - color: hasAmount && !_isLoading ? null : colorScheme.surfaceContainerHighest, - borderRadius: BorderRadius.circular(20), - boxShadow: hasAmount && !_isLoading - ? [ - BoxShadow( - color: goldStart.withValues(alpha: 0.4), - blurRadius: 20, - offset: const Offset(0, 8), - ), - BoxShadow( - color: goldStart.withValues(alpha: 0.2), - blurRadius: 8, - offset: const Offset(0, 4), - ), - ] - : [ - BoxShadow( - color: colorScheme.shadow.withValues(alpha: 0.08), - blurRadius: 12, - offset: const Offset(0, 4), - ), - ], - ), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: _isLoading ? null : _doTransfer, - borderRadius: BorderRadius.circular(20), - child: Center( - child: _isLoading - ? SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - strokeWidth: 2.5, - valueColor: AlwaysStoppedAnimation( - isDark ? Colors.white : colorScheme.onSurface, - ), - ), - ) - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - LucideIcons.arrowRight, - size: 22, - color: hasAmount ? Colors.white : colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Text( - '確認劃轉', - style: TextStyle( - color: hasAmount ? Colors.white : colorScheme.onSurfaceVariant, - fontSize: 18, - fontWeight: FontWeight.w700, - letterSpacing: 0.5, - ), - ), - ], - ), + return SizedBox( + width: double.infinity, + height: 48, + child: ElevatedButton( + onPressed: _isLoading ? null : _doTransfer, + style: ElevatedButton.styleFrom( + backgroundColor: hasAmount + ? colorScheme.onSurface + : colorScheme.surfaceContainerHighest, + foregroundColor: hasAmount + ? colorScheme.surface + : colorScheme.onSurfaceVariant, + disabledBackgroundColor: colorScheme.surfaceContainerHighest, + disabledForegroundColor: colorScheme.onSurfaceVariant, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), ), ), + child: _isLoading + ? SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + colorScheme.onSurfaceVariant, + ), + ), + ) + : Text( + '确认划转', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), ), ); } @@ -857,130 +562,3 @@ class _TransferPageState extends State return val.toStringAsFixed(2); } } - -// ============================================ -// Animated Account Card Widget -// ============================================ - -class _AnimatedAccountCard extends StatelessWidget { - final String label; - final String accountName; - final String balance; - final IconData icon; - final bool isSource; - final Animation animation; - - const _AnimatedAccountCard({ - super.key, - required this.label, - required this.accountName, - required this.balance, - required this.icon, - required this.isSource, - required this.animation, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final isDark = colorScheme.brightness == Brightness.dark; - final goldColor = isDark ? const Color(0xFFD4AF37) : const Color(0xFFF59E0B); - - return AnimatedBuilder( - animation: animation, - builder: (context, child) { - return Transform.translate( - offset: Offset(0, animation.value * (isSource ? -10 : 10)), - child: Opacity( - opacity: 1.0 - (animation.value * 0.3), - child: child, - ), - ); - }, - child: Container( - width: double.infinity, - padding: const EdgeInsets.all(AppSpacing.md), - decoration: BoxDecoration( - color: isDark - ? colorScheme.surfaceContainer.withValues(alpha: 0.5) - : colorScheme.surface.withValues(alpha: 0.8), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: colorScheme.outlineVariant.withValues(alpha: 0.2), - width: 1, - ), - ), - child: Row( - children: [ - // Icon container with gradient - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - goldColor.withValues(alpha: 0.15), - goldColor.withValues(alpha: 0.05), - ], - ), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: goldColor.withValues(alpha: 0.2), - width: 1, - ), - ), - child: Icon( - icon, - size: 22, - color: goldColor, - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: AppTextStyles.bodySmall(context).copyWith( - color: colorScheme.onSurfaceVariant, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 4), - Text( - accountName, - style: AppTextStyles.headlineSmall(context).copyWith( - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '¥ $balance', - style: AppTextStyles.headlineSmall(context).copyWith( - fontWeight: FontWeight.w700, - color: colorScheme.onSurface, - ), - ), - const SizedBox(height: 4), - Text( - 'USDT', - style: AppTextStyles.bodySmall(context).copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/flutter_monisuo/lib/ui/pages/asset/withdraw_page.dart b/flutter_monisuo/lib/ui/pages/asset/withdraw_page.dart index 438024e..ebc71db 100644 --- a/flutter_monisuo/lib/ui/pages/asset/withdraw_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/withdraw_page.dart @@ -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 { final _amountController = TextEditingController(); final _addressController = TextEditingController(); final _contactController = TextEditingController(); - final _formKey = GlobalKey(); + final _amountFocus = FocusNode(); bool _isSubmitting = false; - final _feeNotifier = ValueNotifier('提現將扣除 10% 手續費'); - final _networksNotifier = ValueNotifier>([]); - final _selectedNetworkNotifier = ValueNotifier(null); + List _networks = []; + String? _selectedNetwork; @override void initState() { super.initState(); - _amountController.addListener(_onAmountChanged); + _amountController.addListener(() => setState(() {})); _loadNetworks(); } @@ -43,367 +39,468 @@ class _WithdrawPageState extends State { _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().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 _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().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( - 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>( - 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( - valueListenable: _selectedNetworkNotifier, - builder: (_, selected, __) { - return SizedBox( - width: double.infinity, - child: ShadSelect( - 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( + 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( + 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, + ), + ), + ], + ), + ); + } } diff --git a/flutter_monisuo/lib/ui/pages/mine/components/logout_button.dart b/flutter_monisuo/lib/ui/pages/mine/components/logout_button.dart index 893ee15..2a9cd87 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/logout_button.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/logout_button.dart @@ -1,34 +1,29 @@ import 'package:flutter/material.dart'; -import '../../../../core/theme/app_color_scheme.dart'; -import '../../../../core/theme/app_spacing.dart'; -import '../../../../core/theme/app_theme.dart'; -/// 退出登錄按鈕 +/// 退出登录按钮 class LogoutButton extends StatelessWidget { final VoidCallback onLogout; const LogoutButton({super.key, required this.onLogout}); @override Widget build(BuildContext context) { - return GestureDetector( - onTap: onLogout, - child: Container( - width: double.infinity, - height: 48, - decoration: BoxDecoration( - color: AppColorScheme.down.withOpacity(0.05), - borderRadius: BorderRadius.circular(AppRadius.lg), - border: Border.all( - color: AppColorScheme.down.withOpacity(0.15), + final colorScheme = Theme.of(context).colorScheme; + + return SizedBox( + width: double.infinity, + height: 48, + child: OutlinedButton( + onPressed: onLogout, + style: OutlinedButton.styleFrom( + foregroundColor: colorScheme.error, + side: BorderSide(color: colorScheme.error), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), ), ), - child: Center( - child: Text( - '退出登錄', - style: AppTextStyles.headlineMedium(context).copyWith( - color: AppColorScheme.down, - ), - ), + child: const Text( + '退出登錄', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), ), ), ); diff --git a/flutter_monisuo/lib/ui/pages/mine/components/menu_group1.dart b/flutter_monisuo/lib/ui/pages/mine/components/menu_group1.dart index 3226e22..06b8128 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/menu_group1.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/menu_group1.dart @@ -1,16 +1,12 @@ import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.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_extension.dart'; import '../kyc_page.dart'; import '../welfare_center_page.dart'; import 'menu_group_container.dart'; import 'menu_row.dart'; import 'menu_trailing_widgets.dart'; -/// 菜單分組1 - 福利中心 / 實名認證 / 安全設置 / 消息通知 +/// 菜单分组1 - 福利中心 / 实名认证 / 安全设置 / 消息通知 class MenuGroup1 extends StatelessWidget { final int kycStatus; final void Function(String) onShowComingSoon; @@ -23,13 +19,14 @@ class MenuGroup1 extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return MenuGroupContainer( child: Column( children: [ - // 福利中心 MenuRow( icon: LucideIcons.gift, - iconColor: AppColorScheme.darkSecondary, // gold + iconColor: colorScheme.secondary, title: '福利中心', onTap: () { Navigator.push( @@ -39,15 +36,14 @@ class MenuGroup1 extends StatelessWidget { }, ), const Divider(height: 1), - // 實名認證 MenuRow( icon: LucideIcons.shieldCheck, - iconColor: context.appColors.up, + iconColor: colorScheme.secondary, title: '實名認證', trailing: KycBadge(kycStatus: kycStatus), onTap: () { if (kycStatus == 2) { - showKycStatusDialog(context); + _showKycStatusDialog(context); } else { Navigator.push( context, @@ -57,18 +53,16 @@ class MenuGroup1 extends StatelessWidget { }, ), const Divider(height: 1), - // 安全設置 MenuRow( icon: LucideIcons.lock, - iconColor: context.colors.onSurfaceVariant, + iconColor: colorScheme.onSurfaceVariant, title: '安全設置', onTap: () => onShowComingSoon('安全設置'), ), const Divider(height: 1), - // 消息通知 MenuRow( icon: LucideIcons.bell, - iconColor: context.colors.onSurfaceVariant, + iconColor: colorScheme.onSurfaceVariant, title: '消息通知', trailing: const RedDotIndicator(), onTap: () => onShowComingSoon('消息通知'), @@ -79,23 +73,16 @@ class MenuGroup1 extends StatelessWidget { } } -/// 顯示 KYC 認證狀態對話框 -void showKycStatusDialog(BuildContext context) { - showShadDialog( +void _showKycStatusDialog(BuildContext context) { + showDialog( context: context, - builder: (ctx) => ShadDialog.alert( - title: Row( - children: [ - Icon(Icons.check_circle, color: AppColorScheme.up, size: 20), - SizedBox(width: AppSpacing.sm), - const Text('實名認證'), - ], - ), - description: const Text('您的實名認證已通過'), + builder: (ctx) => AlertDialog( + title: const Text('實名認證'), + content: const Text('您的實名認證已通過'), actions: [ - ShadButton( - child: const Text('確定'), + TextButton( onPressed: () => Navigator.of(ctx).pop(), + child: const Text('确定'), ), ], ), diff --git a/flutter_monisuo/lib/ui/pages/mine/components/menu_group_container.dart b/flutter_monisuo/lib/ui/pages/mine/components/menu_group_container.dart index 714dadb..fb22137 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/menu_group_container.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/menu_group_container.dart @@ -1,11 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../../core/theme/app_spacing.dart'; -import '../../../../core/theme/app_theme_extension.dart'; -/// 菜單分組容器 - 統一的圓角卡片樣式 -/// -/// 所有菜單分組共享相同的容器樣式:背景色、圓角、邊框。 -/// 通過 [child] 傳入菜單項 Column。 +/// 菜单分组容器 class MenuGroupContainer extends StatelessWidget { final Widget child; @@ -13,14 +8,16 @@ class MenuGroupContainer extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( width: double.infinity, clipBehavior: Clip.antiAlias, decoration: BoxDecoration( - color: context.appColors.surfaceCard, - borderRadius: BorderRadius.circular(AppRadius.lg), + color: colorScheme.surface, + borderRadius: BorderRadius.circular(14), border: Border.all( - color: context.appColors.ghostBorder, + color: colorScheme.outlineVariant.withValues(alpha: 0.5), ), ), child: child, diff --git a/flutter_monisuo/lib/ui/pages/mine/components/menu_row.dart b/flutter_monisuo/lib/ui/pages/mine/components/menu_row.dart index 4b27240..e48f4ea 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/menu_row.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/menu_row.dart @@ -1,12 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; -import '../../../../core/theme/app_spacing.dart'; import '../../../../core/theme/app_theme.dart'; -import '../../../../core/theme/app_theme_extension.dart'; -/// 單行菜單項:圖標 + 標題 + 尾部組件 (chevron) -/// -/// 圖標顏色 (通常是使用主題色) +/// 单行菜单项:图标 + 标题 + 尾部 class MenuRow extends StatelessWidget { final IconData icon; final Color iconColor; @@ -25,38 +21,30 @@ class MenuRow extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ - // Icon in 36x36 rounded container - Container( - width: 36, - height: 36, - decoration: BoxDecoration( - color: context.appColors.surfaceCardHigh, - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Icon(icon, size: 18, color: iconColor), - ), - ), + Icon(icon, size: 18, color: iconColor), const SizedBox(width: 10), - // Title Expanded( child: Text( title, - style: AppTextStyles.headlineMedium(context), + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w500, + ), ), ), - // Trailing - if (trailing != null) + if (trailing != null) trailing!, + if (trailing == null) Icon( LucideIcons.chevronRight, size: 16, - color: context.colors.onSurfaceVariant, + color: colorScheme.onSurfaceVariant, ), ], ), diff --git a/flutter_monisuo/lib/ui/pages/mine/components/menu_trailing_widgets.dart b/flutter_monisuo/lib/ui/pages/mine/components/menu_trailing_widgets.dart index 055ecb4..52acb9f 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/menu_trailing_widgets.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/menu_trailing_widgets.dart @@ -1,178 +1,96 @@ import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:provider/provider.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 '../../../../providers/theme_provider.dart'; -/// KYC 狀態徽章 (e.g. "已認證" green badge + chevron) -/// -/// 根據 [kycStatus] 顯示不同狀態: -/// - 2: 已認證(綠色) -/// - 1: 審核中(橙色) -/// - 其他: 僅顯示 chevron +/// KYC 状态文字 class KycBadge extends StatelessWidget { final int kycStatus; const KycBadge({super.key, required this.kycStatus}); @override Widget build(BuildContext context) { - final green = context.appColors.up; + final colorScheme = Theme.of(context).colorScheme; if (kycStatus == 2) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), - decoration: BoxDecoration( - color: green.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(AppRadius.sm), - ), - child: Text( - '已認證', - style: AppTextStyles.labelMedium(context).copyWith( - color: green, - ), - ), - ), - const SizedBox(width: 8), - Icon( - LucideIcons.chevronRight, - size: 16, - color: context.colors.onSurfaceVariant, - ), - ], + return Text( + '已認證', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.secondary, + fontWeight: FontWeight.w500, + ), ); } if (kycStatus == 1) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), - decoration: BoxDecoration( - color: AppColorScheme.warning.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(AppRadius.sm), - ), - child: Text( - '審核中', - style: AppTextStyles.labelMedium(context).copyWith( - color: AppColorScheme.warning, - ), - ), - ), - const SizedBox(width: 8), - Icon( - LucideIcons.chevronRight, - size: 16, - color: context.colors.onSurfaceVariant, - ), - ], + return Text( + '審核中', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.tertiary, + fontWeight: FontWeight.w500, + ), ); } - return Icon( - LucideIcons.chevronRight, - size: 16, - color: context.colors.onSurfaceVariant, + return Text( + '未認證', + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, + ), ); } } -/// 紅點指示器 - 消息通知 + chevron +/// 红点指示器 class RedDotIndicator extends StatelessWidget { const RedDotIndicator({super.key}); @override Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 8, - height: 8, - decoration: BoxDecoration( - color: AppColorScheme.down, - shape: BoxShape.circle, - ), - ), - const SizedBox(width: 8), - Icon( - LucideIcons.chevronRight, - size: 16, - color: context.colors.onSurfaceVariant, - ), - ], + final colorScheme = Theme.of(context).colorScheme; + return Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: colorScheme.error, + shape: BoxShape.circle, + ), ); } } -/// 深色模式切換行 +/// 深色模式切换行 class DarkModeRow extends StatelessWidget { const DarkModeRow({super.key}); @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final themeProvider = context.watch(); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ - // Icon in 36x36 rounded container - Container( - width: 36, - height: 36, - decoration: BoxDecoration( - color: context.appColors.surfaceCardHigh, - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Icon( - LucideIcons.moon, - size: 18, - color: context.colors.onSurfaceVariant, - ), - ), + Icon( + LucideIcons.moon, + size: 18, + color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 10), Expanded( child: Text( '深色模式', - style: AppTextStyles.headlineMedium(context), - ), - ), - // Toggle switch - matching .pen design (44x24 rounded pill) - GestureDetector( - onTap: () => themeProvider.toggleTheme(), - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - width: 44, - height: 24, - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: context.appColors.surfaceCardHigh, - borderRadius: BorderRadius.circular(12), - ), - child: AnimatedAlign( - duration: const Duration(milliseconds: 200), - alignment: - themeProvider.isDarkMode - ? Alignment.centerRight : Alignment.centerLeft, - child: Container( - width: 20, - height: 20, - decoration: BoxDecoration( - color: context.colors.onSurface, - shape: BoxShape.circle, - ), + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w500, ), ), - ), + ), + Switch( + value: themeProvider.isDarkMode, + onChanged: (_) => themeProvider.toggleTheme(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ], ), diff --git a/flutter_monisuo/lib/ui/pages/mine/components/profile_card.dart b/flutter_monisuo/lib/ui/pages/mine/components/profile_card.dart index acd39a2..4c01c81 100644 --- a/flutter_monisuo/lib/ui/pages/mine/components/profile_card.dart +++ b/flutter_monisuo/lib/ui/pages/mine/components/profile_card.dart @@ -1,60 +1,59 @@ import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; -import '../../../../core/theme/app_spacing.dart'; import '../../../../core/theme/app_theme.dart'; -import '../../../../core/theme/app_theme_extension.dart'; import 'avatar_circle.dart'; -/// 用戶資料卡片 - 頭像 + 用戶名 + 徽章 + chevron +/// 用户资料卡片 class ProfileCard extends StatelessWidget { final dynamic user; const ProfileCard({super.key, required this.user}); @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( width: double.infinity, - padding: const EdgeInsets.all(20), + padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: context.appColors.surfaceCard, - borderRadius: BorderRadius.circular(AppRadius.lg), + color: colorScheme.surface, + borderRadius: BorderRadius.circular(14), border: Border.all( - color: context.appColors.ghostBorder, + color: colorScheme.outlineVariant.withValues(alpha: 0.5), ), ), child: Row( children: [ - // Avatar AvatarCircle( - radius: 24, - fontSize: 18, + radius: 22, + fontSize: 16, text: user?.avatarText, ), const SizedBox(width: 12), - // Name + badge column Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( user?.username ?? '未登錄', - style: AppTextStyles.headlineLarge(context), + style: AppTextStyles.bodyLarge(context).copyWith( + fontWeight: FontWeight.w600, + ), ), - const SizedBox(height: 4), + const SizedBox(height: 2), Text( '普通用戶', - style: AppTextStyles.bodyMedium(context).copyWith( - fontWeight: FontWeight.normal, + style: AppTextStyles.bodySmall(context).copyWith( + color: colorScheme.onSurfaceVariant, ), ), ], ), ), - // Chevron Icon( LucideIcons.chevronRight, size: 16, - color: context.colors.onSurfaceVariant, + color: colorScheme.onSurfaceVariant, ), ], ), diff --git a/flutter_monisuo/lib/ui/pages/mine/mine_page.dart b/flutter_monisuo/lib/ui/pages/mine/mine_page.dart index c55e418..b139e98 100644 --- a/flutter_monisuo/lib/ui/pages/mine/mine_page.dart +++ b/flutter_monisuo/lib/ui/pages/mine/mine_page.dart @@ -1,19 +1,16 @@ import 'package:flutter/material.dart'; -import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:provider/provider.dart'; -import '../../../core/theme/app_color_scheme.dart'; -import '../../../core/theme/app_spacing.dart'; import '../../../core/theme/app_theme.dart'; +import '../../../core/theme/app_spacing.dart'; import '../../../providers/auth_provider.dart'; import '../auth/login_page.dart'; -import 'components/about_dialog_helpers.dart'; import 'components/avatar_circle.dart'; import 'components/logout_button.dart'; import 'components/menu_group1.dart'; import 'components/menu_group2.dart'; import 'components/profile_card.dart'; -/// 我的頁面 - 匹配 .pen 設計稿 +/// 我的页面 class MinePage extends StatefulWidget { const MinePage({super.key}); @@ -32,7 +29,7 @@ class _MinePageState extends State final colorScheme = Theme.of(context).colorScheme; return Scaffold( - backgroundColor: colorScheme.background, + backgroundColor: colorScheme.surface, body: Consumer( builder: (context, auth, _) { return SingleChildScrollView( @@ -55,12 +52,6 @@ class _MinePageState extends State SizedBox(height: AppSpacing.lg), LogoutButton(onLogout: () => _handleLogout(auth)), SizedBox(height: AppSpacing.md), - Text( - 'System Build v1.0.0', - style: AppTextStyles.bodySmall(context).copyWith( - color: colorScheme.onSurfaceVariant.withOpacity(0.5), - ), - ), ], ), ); @@ -70,21 +61,15 @@ class _MinePageState extends State } void _showComingSoon(String feature) { - showShadDialog( + showDialog( context: context, - builder: (context) => ShadDialog.alert( - title: Row( - children: [ - Icon(Icons.construction, color: AppColorScheme.warning, size: 20), - SizedBox(width: AppSpacing.sm), - const Text('功能開發中'), - ], - ), - description: Text('$feature功能正在開發中,敬請期待~'), + builder: (ctx) => AlertDialog( + title: const Text('功能開發中'), + content: Text('$feature功能正在開發中,敬請期待'), actions: [ - ShadButton( + TextButton( + onPressed: () => Navigator.of(ctx).pop(), child: const Text('知道了'), - onPressed: () => Navigator.of(context).pop(), ), ], ), @@ -92,37 +77,29 @@ class _MinePageState extends State } void _showAboutDialog() { - final colorScheme = Theme.of(context).colorScheme; - showShadDialog( + showDialog( context: context, - builder: (context) => ShadDialog( + builder: (ctx) => AlertDialog( title: Row( children: [ - AvatarCircle(radius: 20, fontSize: 16), - SizedBox(width: AppSpacing.sm + AppSpacing.xs), + AvatarCircle(radius: 16, fontSize: 12), + const SizedBox(width: 8), const Text('模擬所'), ], ), - child: Column( + content: const Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - '虛擬貨幣模擬交易平臺', - style: TextStyle(color: colorScheme.onSurfaceVariant), - ), - SizedBox(height: AppSpacing.md), - InfoRow(icon: Icons.code, text: '版本: 1.0.0'), - SizedBox(height: AppSpacing.sm), - InfoRow( - icon: Icons.favorite, - text: 'Built with Flutter & Material Design 3'), + Text('虛擬貨幣模擬交易平臺'), + SizedBox(height: 8), + Text('版本: 1.0.0'), ], ), actions: [ - ShadButton( - child: const Text('確定'), - onPressed: () => Navigator.of(context).pop(), + TextButton( + onPressed: () => Navigator.of(ctx).pop(), + child: const Text('确定'), ), ], ), @@ -130,18 +107,19 @@ class _MinePageState extends State } void _handleLogout(AuthProvider auth) { - showShadDialog( + final colorScheme = Theme.of(context).colorScheme; + showDialog( context: context, - builder: (ctx) => ShadDialog.alert( - title: const Text('確認退出'), - description: const Text('確定要退出登錄嗎?'), + builder: (ctx) => AlertDialog( + title: const Text('退出登錄'), + content: const Text('確定要退出登錄嗎?'), actions: [ - ShadButton.outline( - child: const Text('取消'), + TextButton( onPressed: () => Navigator.of(ctx).pop(), + child: Text('取消', + style: TextStyle(color: colorScheme.onSurfaceVariant)), ), - ShadButton.destructive( - child: const Text('退出'), + TextButton( onPressed: () async { Navigator.of(ctx).pop(); await auth.logout(); @@ -152,6 +130,8 @@ class _MinePageState extends State ); } }, + child: Text('退出', style: TextStyle(color: colorScheme.error)), + ), ], ),