| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- import 'package:flutter/material.dart';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'package:go_router/go_router.dart';
- import '../../../core/l10n/app_localizations.dart';
- import '../../../core/theme/app_colors.dart';
- import '../../../core/utils/number_format.dart';
- import '../../../data/models/home/top_trader.dart';
- import '../../../providers/auth_provider.dart';
- import '../../../providers/top_trader_provider.dart';
- /// 顶级交易专家横向卡片区块
- class TopTradersSection extends ConsumerWidget {
- const TopTradersSection({super.key});
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final cs = Theme.of(context).colorScheme;
- final state = ref.watch(topTraderProvider);
- if (state.isLoading) {
- return const Padding(
- padding: EdgeInsets.symmetric(vertical: 24),
- child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
- );
- }
- if (state.traders.isEmpty) return const SizedBox.shrink();
- return Column(
- children: [
- // 标题
- Padding(
- padding: const EdgeInsets.fromLTRB(16, 8, 16, 12),
- child: Row(
- children: [
- Text(
- AppLocalizations.of(context)!.topTraders,
- style: TextStyle(
- color: cs.onSurface,
- fontSize: 17,
- fontWeight: FontWeight.w700,
- ),
- ),
- const Spacer(),
- GestureDetector(
- onTap: () => context.go('/copy-trading'),
- child: Icon(Icons.chevron_right,
- color: cs.onSurface.withAlpha(100), size: 22),
- ),
- ],
- ),
- ),
- // 交易专家卡片列表
- SizedBox(
- height: 240,
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- padding: const EdgeInsets.symmetric(horizontal: 16),
- itemCount: state.traders.length,
- separatorBuilder: (_, __) => const SizedBox(width: 12),
- itemBuilder: (context, index) =>
- _TraderCard(trader: state.traders[index]),
- ),
- ),
- const SizedBox(height: 16),
- ],
- );
- }
- }
- // ── 交易员卡片 ─────────────────────────────────────────────
- class _TraderCard extends ConsumerWidget {
- const _TraderCard({required this.trader});
- final TopTrader trader;
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final cs = Theme.of(context).colorScheme;
- final isDark = Theme.of(context).brightness == Brightness.dark;
- final screenWidth = MediaQuery.of(context).size.width;
- // 左边距16 + 右侧留出约36px的下一张预览 + 间距12
- final cardWidth = screenWidth - 16 - 12 - 36;
- return Container(
- width: cardWidth,
- padding: const EdgeInsets.fromLTRB(16, 20, 16, 14),
- decoration: BoxDecoration(
- color: isDark ? AppColors.darkBgSecondary : AppColors.lightBgSecondary,
- borderRadius: BorderRadius.circular(16),
- border: Border.all(color: cs.outline.withAlpha(30)),
- ),
- child: Column(
- children: [
- // 头像:深色圆形底 + 头像/字母
- Container(
- width: 60,
- height: 60,
- decoration: BoxDecoration(
- color: AppColors.darkAvatarBg,
- shape: BoxShape.circle,
- border: Border.all(color: AppColors.brand, width: 2.5),
- ),
- child: trader.avatar.isNotEmpty
- ? ClipOval(
- child: Image.network(
- trader.avatar,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _AvatarLetter(trader: trader),
- ),
- )
- : _AvatarLetter(trader: trader),
- ),
- const SizedBox(height: 10),
- // 昵称
- Text(
- trader.nickname,
- style: TextStyle(
- color: cs.onSurface,
- fontSize: 15,
- fontWeight: FontWeight.w600,
- ),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- const SizedBox(height: 14),
- // 数据行
- Row(
- children: [
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- AppLocalizations.of(context)!.twoWeekReturn,
- style: TextStyle(
- color: cs.onSurface.withAlpha(120), fontSize: 11),
- ),
- const SizedBox(height: 4),
- Text(
- '${trader.dayYield30 >= 0 ? '+' : ''}${formatPrice(trader.dayYield30, decimalPlaces: 2)}%',
- style: TextStyle(
- color: AppColors.changeColor(trader.dayYield30),
- fontSize: 16,
- fontWeight: FontWeight.w700,
- ),
- ),
- ],
- ),
- ),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Text(
- AppLocalizations.of(context)!.twoWeekCopyIncome,
- style: TextStyle(
- color: cs.onSurface.withAlpha(120), fontSize: 11),
- ),
- const SizedBox(height: 4),
- Text(
- trader.profitAmount != 0
- ? '${trader.profitAmount >= 0 ? '+' : ''}${formatPrice(trader.profitAmount, decimalPlaces: 2)}'
- : '--',
- style: TextStyle(
- color: trader.profitAmount != 0
- ? AppColors.changeColor(trader.profitAmount)
- : cs.onSurface,
- fontSize: 14,
- fontWeight: FontWeight.w600,
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- const Spacer(),
- // 跟单按钮
- SizedBox(
- width: double.infinity,
- height: 40,
- child: ElevatedButton(
- onPressed: () {
- if (!ref.read(isLoggedInProvider)) {
- context.push('/login');
- return;
- }
- if (trader.id.isNotEmpty) {
- context.push('/trader-detail/${trader.id}');
- } else {
- context.push('/copy-trading');
- }
- },
- style: ElevatedButton.styleFrom(
- backgroundColor: AppColors.brand,
- foregroundColor: Colors.black,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(10),
- ),
- elevation: 0,
- ),
- child: Text(
- AppLocalizations.of(context)!.copyTrading,
- style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
- ),
- ),
- ),
- ],
- ),
- );
- }
- }
- class _AvatarLetter extends StatelessWidget {
- const _AvatarLetter({required this.trader});
- final TopTrader trader;
- @override
- Widget build(BuildContext context) {
- return Center(
- child: Text(
- trader.avatarLetter,
- style: const TextStyle(
- color: Colors.white,
- fontSize: 22,
- fontWeight: FontWeight.w700,
- ),
- ),
- );
- }
- }
|