import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../core/utils/avatar_urls.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/utils/dialog_utils.dart'; import '../../../core/utils/top_toast.dart'; import '../../../data/repositories/copy_trading_repository.dart'; class FollowSettingScreen extends ConsumerStatefulWidget { const FollowSettingScreen({super.key, required this.trader}); /// 从 TraderDetailScreen 传入的完整 trader info map final Map trader; @override ConsumerState createState() => _FollowSettingScreenState(); } class _FollowSettingScreenState extends ConsumerState { List> _symbols = []; bool _loadingSymbols = true; bool _submitting = false; static const _avatarColors = [ Color(0xFFf7931a), Color(0xFF627eea), Color(0xFF9945ff), Color(0xFFf3ba2f), Color(0xFF2775ca), Color(0xFF00aae4), ]; @override void initState() { super.initState(); _loadSymbols(); } Future _loadSymbols() async { final traderId = widget.trader['id']?.toString() ?? ''; if (traderId.isEmpty) { setState(() => _loadingSymbols = false); return; } try { final list = await ref .read(copyTradingRepositoryProvider) .getTraderSymbols(traderId); if (mounted) { setState(() { _symbols = list; _loadingSymbols = false; }); } } catch (_) { if (mounted) setState(() => _loadingSymbols = false); } } Future _submit() async { if (_submitting) return; setState(() => _submitting = true); final traderId = widget.trader['id']?.toString() ?? ''; try { final allSymbolNames = _symbols .map((s) => s['symbolName']?.toString() ?? s['symbol']?.toString() ?? '') .where((n) => n.isNotEmpty) .toList(); await ref.read(copyTradingRepositoryProvider).followTrader({ 'traderId': traderId, 'tradingMode': '30', // 按交易员比例 'tradingAmount': '100', // 100% 跟随 'symbols': allSymbolNames, }); if (mounted) { showTopToast( context, message: AppLocalizations.of(context)!.copyTradingSuccess, backgroundColor: const Color(0xFF2ECC71), ); Navigator.of(context).pop(true); // 返回 true 表示跟单成功 } } catch (e) { if (mounted) { setState(() => _submitting = false); showTopToast(context, message: extractErrorMessage(e)); } } } Color _avatarBg(String name) => _avatarColors[ name.isEmpty ? 0 : name.codeUnitAt(0) % _avatarColors.length]; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final pageBg = isDark ? AppColors.darkBg : AppColors.lightBg; final nickname = widget.trader['nickname']?.toString() ?? ''; final levelName = widget.trader['levelName']?.toString() ?? ''; final avatarUrl = resolvedAvatarUrlFromRecord(Map.from(widget.trader)); final letter = nickname.isNotEmpty ? nickname[0].toUpperCase() : '?'; final moneyStrength = widget.trader['moneyStrength']?.toString() ?? '--'; final registerDays = widget.trader['registerDays']?.toString() ?? '--'; return Scaffold( backgroundColor: pageBg, appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios, size: 18), onPressed: () => Navigator.of(context).pop(), ), title: Text(AppLocalizations.of(context)!.copyTradingSettings, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)), ), body: Column( children: [ Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── 交易员信息卡片 ────────────────────────────── Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: isDark ? AppColors.darkBgSecondary : AppColors.lightBg, borderRadius: BorderRadius.circular(12), border: Border.all( color: isDark ? AppColors.darkDivider : AppColors.lightDivider, width: 0.5, ), ), child: Row( children: [ // 头像 _Avatar( letter: letter, avatarUrl: avatarUrl, bg: _avatarBg(nickname), size: 48, ), const SizedBox(width: 12), // 名称 + 等级 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( nickname, style: TextStyle( color: cs.onSurface, fontSize: 15, fontWeight: FontWeight.w700, ), ), if (levelName.isNotEmpty) ...[ const SizedBox(height: 4), _LevelBadge(level: levelName), ], ], ), ), // 右侧:资金实力 / 入驻天数 Builder(builder: (context) { final l10n = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ _RightStat( label: l10n.fundStrength, value: '≥$moneyStrength'), const SizedBox(height: 6), _RightStat( label: l10n.settledDaysTitle, value: registerDays), ], ); }), ], ), ), const SizedBox(height: 20), // ── 跟单合约 ────────────────────────────────── Text( AppLocalizations.of(context)!.tradingContracts, style: TextStyle( color: cs.onSurface.withAlpha(153), fontSize: 13, ), ), const SizedBox(height: 10), if (_loadingSymbols) const Center( child: CircularProgressIndicator(color: AppColors.brand)) else if (_symbols.isEmpty) Text( AppLocalizations.of(context)!.noCopyContracts, style: TextStyle( color: cs.onSurface.withAlpha(120), fontSize: 13), ) else Wrap( spacing: 8, runSpacing: 8, children: _symbols.map((s) { final name = s['symbolName']?.toString() ?? s['symbol']?.toString() ?? ''; return Container( padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 7), decoration: BoxDecoration( color: AppColors.brand.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(8), border: Border.all( color: AppColors.brand.withValues(alpha: 0.6), width: 1.5, ), ), child: Text( name, style: const TextStyle( color: AppColors.brand, fontSize: 13, fontWeight: FontWeight.w600, ), ), ); }).toList(), ), ], ), ), ), // ── 底部按钮 ─────────────────────────────────── Container( padding: EdgeInsets.fromLTRB( 16, 12, 16, 12 + MediaQuery.of(context).padding.bottom), decoration: BoxDecoration( color: isDark ? AppColors.darkBgSecondary : AppColors.lightBg, border: Border( top: BorderSide( color: isDark ? AppColors.darkDivider : AppColors.lightDivider, ), ), ), child: SizedBox( width: double.infinity, height: 48, child: ElevatedButton( onPressed: _submitting ? null : _submit, style: ElevatedButton.styleFrom( backgroundColor: AppColors.brand, foregroundColor: Colors.black, shape: const StadiumBorder(), elevation: 0, disabledBackgroundColor: AppColors.brand.withAlpha(60), ), child: _submitting ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.black), ) : Text(AppLocalizations.of(context)!.startCopyTrading, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600)), ), ), ), ], ), ); } } // ── 头像 ────────────────────────────────────────────────── class _Avatar extends StatelessWidget { const _Avatar( {required this.letter, required this.bg, required this.size, this.avatarUrl}); final String letter; final Color bg; final double size; final String? avatarUrl; @override Widget build(BuildContext context) { if (avatarUrl != null && avatarUrl!.isNotEmpty) { return ClipOval( child: Image.network( avatarUrl!, width: size, height: size, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _fallback(), ), ); } return _fallback(); } Widget _fallback() => Container( width: size, height: size, decoration: BoxDecoration(color: bg, shape: BoxShape.circle), child: Center( child: Text( letter, style: TextStyle( color: Colors.white, fontSize: size * 0.4, fontWeight: FontWeight.w700, ), ), ), ); } // ── 等级角标 ─────────────────────────────────────────────── class _LevelBadge extends StatelessWidget { const _LevelBadge({required this.level}); final String level; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: AppColors.brand.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.access_time, size: 10, color: AppColors.brand), const SizedBox(width: 3), Text(level, style: const TextStyle( color: AppColors.brand, fontSize: 11, fontWeight: FontWeight.w600)), ], ), ); } } // ── 右侧统计项 ───────────────────────────────────────────── class _RightStat extends StatelessWidget { const _RightStat({required this.label, required this.value}); final String label; final String value; @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(label, style: TextStyle(color: cs.onSurface.withAlpha(120), fontSize: 11)), const SizedBox(height: 2), Text(value, style: TextStyle( color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w700)), ], ); } }