import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:provider/provider.dart'; import 'package:flutter_animate/flutter_animate.dart'; import '../../../core/theme/app_color_scheme.dart'; import '../../../core/theme/app_spacing.dart'; import '../../../providers/asset_provider.dart'; import '../../../providers/auth_provider.dart'; import '../../shared/ui_constants.dart'; /// 首页 - 使用 shadcn_ui 现代化设计 class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } void _loadData() { final provider = context.read(); provider.loadOverview(); provider.loadTradeAccount(); } @override Widget build(BuildContext context) { super.build(context); final theme = ShadTheme.of(context); return Scaffold( backgroundColor: theme.colorScheme.background, body: Consumer( builder: (context, provider, _) { return RefreshIndicator( onRefresh: () => provider.refreshAll(force: true), color: theme.colorScheme.primary, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: AppSpacing.pagePadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: AppSpacing.sm), _Header(), SizedBox(height: AppSpacing.lg + AppSpacing.sm), _AssetOverviewCard(overview: provider.overview), SizedBox(height: AppSpacing.md), _QuickActions( onDeposit: _showDeposit, onWithdraw: _showWithdraw, onTransfer: _showTransfer, onTrade: _navigateToTrade, ), SizedBox(height: AppSpacing.lg), _HoldingsList(holdings: provider.holdings), ], ), ), ); }, ), ); } void _showDeposit() => _showAmountDialog('充值', (amount) { context.read().deposit(amount: amount); }); void _showWithdraw() { final amountController = TextEditingController(); final addressController = TextEditingController(); final contactController = TextEditingController(); final formKey = GlobalKey(); showShadDialog( context: context, builder: (ctx) => ShadDialog( title: const Text('提现'), child: ShadForm( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ ShadInputFormField( id: 'amount', placeholder: const Text('请输入提现金额(USDT)'), controller: amountController, keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: Validators.amount, ), SizedBox(height: AppSpacing.sm + AppSpacing.xs), ShadInputFormField( id: 'address', placeholder: const Text('请输入提现地址'), controller: addressController, validator: (v) => Validators.required(v, '提现地址'), ), SizedBox(height: AppSpacing.sm + AppSpacing.xs), ShadInputFormField( id: 'contact', placeholder: const Text('联系方式(可选)'), controller: contactController, ), ], ), ), actions: [ ShadButton.outline(child: const Text('取消'), onPressed: () => Navigator.of(ctx).pop()), ShadButton( child: const Text('确认'), onPressed: () { if (formKey.currentState!.saveAndValidate()) { Navigator.of(ctx).pop(); context.read().withdraw( amount: amountController.text.trim(), withdrawAddress: addressController.text.trim(), withdrawContact: contactController.text.trim().isEmpty ? null : contactController.text.trim(), ); } }, ), ], ), ); } void _showTransfer() => _showAmountDialog('划转', (amount) { context.read().transfer(direction: 1, amount: amount); }); void _showAmountDialog(String title, Function(String) onSubmit) { final controller = TextEditingController(); final formKey = GlobalKey(); showShadDialog( context: context, builder: (ctx) => ShadDialog( title: Text(title), child: ShadForm( key: formKey, child: ShadInputFormField( id: 'amount', placeholder: Text('请输入${title}金额(USDT)'), controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: Validators.amount, ), ), actions: [ ShadButton.outline(child: const Text('取消'), onPressed: () => Navigator.of(ctx).pop()), ShadButton( child: const Text('确认'), onPressed: () { if (formKey.currentState!.saveAndValidate()) { onSubmit(controller.text); Navigator.of(ctx).pop(); } }, ), ], ), ); } void _navigateToTrade() { // 切换到交易页 - 通过 MainController } } /// 头部组件 class _Header extends StatelessWidget { @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Consumer( builder: (context, auth, _) { final user = auth.user; return Row( children: [ _Avatar(text: user?.avatarText), SizedBox(width: AppSpacing.sm + AppSpacing.xs), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '你好,${user?.username ?? '用户'}', style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold), ), SizedBox(height: AppSpacing.xs), Text('欢迎来到模拟所', style: theme.textTheme.muted), ], ), ), ], ).animate().fadeIn(duration: 300.ms).slideX(begin: -0.1, end: 0); }, ); } } /// 头像组件 class _Avatar extends StatelessWidget { final String? text; const _Avatar({this.text}); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return CircleAvatar( radius: 20, backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.2), child: Text( text ?? 'U', style: TextStyle(color: theme.colorScheme.primary, fontWeight: FontWeight.bold), ), ); } } /// 资产总览卡片 class _AssetOverviewCard extends StatelessWidget { final dynamic overview; const _AssetOverviewCard({required this.overview}); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Container( width: double.infinity, padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.sm), decoration: BoxDecoration( gradient: AppColorScheme.assetCardGradient, borderRadius: BorderRadius.circular(AppRadius.xl), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('总资产(USDT)', style: theme.textTheme.small.copyWith(color: Colors.white70)), SizedBox(height: AppSpacing.sm), Text( overview?.totalAsset ?? '0.00', style: theme.textTheme.h2.copyWith(color: Colors.white, fontWeight: FontWeight.bold), ), SizedBox(height: AppSpacing.lg + AppSpacing.sm), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _AssetItem(label: '资金账户', value: overview?.fundBalance ?? '0.00'), _AssetItem(label: '交易账户', value: overview?.tradeBalance ?? '0.00'), ], ), ], ), ).animate().fadeIn(duration: 400.ms).slideY(begin: 0.1, end: 0); } } /// 资产项 class _AssetItem extends StatelessWidget { final String label; final String value; const _AssetItem({required this.label, required this.value}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontSize: 12, color: Colors.white70)), SizedBox(height: AppSpacing.xs), Text(value, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white)), ], ); } } /// 快捷操作 class _QuickActions extends StatelessWidget { final VoidCallback onDeposit; final VoidCallback onWithdraw; final VoidCallback onTransfer; final VoidCallback onTrade; const _QuickActions({ required this.onDeposit, required this.onWithdraw, required this.onTransfer, required this.onTrade, }); @override Widget build(BuildContext context) { return ShadCard( padding: AppSpacing.cardPadding, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _ActionButton(icon: LucideIcons.arrowDownToLine, text: '充值', color: AppColorScheme.success, onTap: onDeposit), _ActionButton(icon: LucideIcons.arrowUpFromLine, text: '提现', color: AppColorScheme.warning, onTap: onWithdraw), _ActionButton(icon: LucideIcons.arrowRightLeft, text: '划转', color: AppColorScheme.info, onTap: onTransfer), _ActionButton(icon: LucideIcons.trendingUp, text: '交易', color: AppColorScheme.info, onTap: onTrade), ], ), ).animate().fadeIn(duration: 500.ms, delay: 100.ms); } } /// 操作按钮 class _ActionButton extends StatelessWidget { final IconData icon; final String text; final Color color; final VoidCallback onTap; const _ActionButton({ required this.icon, required this.text, required this.color, required this.onTap, }); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return GestureDetector( onTap: onTap, child: Column( children: [ Container( width: 48, height: 48, decoration: BoxDecoration(color: color.withValues(alpha: 0.15), shape: BoxShape.circle), child: Icon(icon, color: color, size: 22), ), SizedBox(height: AppSpacing.sm), Text(text, style: TextStyle(fontSize: 12, color: theme.colorScheme.foreground)), ], ), ); } } /// 持仓列表 class _HoldingsList extends StatelessWidget { final List holdings; const _HoldingsList({required this.holdings}); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return ShadCard( padding: AppSpacing.cardPadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('我的持仓', style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold)), Icon(LucideIcons.chevronRight, color: theme.colorScheme.mutedForeground, size: 20), ], ), SizedBox(height: AppSpacing.md), if (holdings.isEmpty) _EmptyHoldings() else _HoldingsListView(holdings: holdings), ], ), ).animate().fadeIn(duration: 500.ms, delay: 200.ms); } } /// 空持仓提示 class _EmptyHoldings extends StatelessWidget { @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Center( child: Padding( padding: EdgeInsets.all(AppSpacing.xl), child: Column( children: [ Icon(LucideIcons.wallet, size: 48, color: theme.colorScheme.mutedForeground), SizedBox(height: AppSpacing.sm + AppSpacing.xs), Text('暂无持仓', style: theme.textTheme.muted), SizedBox(height: AppSpacing.xs), Text('快去交易吧~', style: theme.textTheme.muted.copyWith(fontSize: 12)), ], ), ), ); } } /// 持仓列表视图 class _HoldingsListView extends StatelessWidget { final List holdings; const _HoldingsListView({required this.holdings}); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); final displayHoldings = holdings.length > 5 ? holdings.sublist(0, 5) : holdings; return ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: displayHoldings.length, separatorBuilder: (_, __) => Divider(color: theme.colorScheme.border, height: 1), itemBuilder: (context, index) { return _HoldingItem(holding: displayHoldings[index]) .animate() .fadeIn(delay: Duration(milliseconds: 50 * index)); }, ); } } /// 持仓项 class _HoldingItem extends StatelessWidget { final dynamic holding; const _HoldingItem({required this.holding}); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return Padding( padding: EdgeInsets.symmetric(vertical: AppSpacing.sm), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ CircleAvatar( radius: 18, backgroundColor: theme.colorScheme.primary.withValues(alpha: 0.1), child: Text( holding.coinCode.substring(0, 1), style: TextStyle(color: theme.colorScheme.primary, fontWeight: FontWeight.bold), ), ), SizedBox(width: AppSpacing.sm + AppSpacing.xs), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(holding.coinCode, style: theme.textTheme.large.copyWith(fontWeight: FontWeight.bold)), Text(holding.quantity, style: theme.textTheme.muted), ], ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('${holding.currentValue} USDT', style: theme.textTheme.small.copyWith(fontWeight: FontWeight.w500)), Text( holding.formattedProfitRate, style: TextStyle( color: holding.isProfit ? AppColorScheme.up : AppColorScheme.down, fontSize: 12, ), ), ], ), ], ), ); } }