import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:flutter_animate/flutter_animate.dart'; import '../../core/theme/app_color_scheme.dart'; import '../../core/theme/app_spacing.dart'; /// 资产卡片组件 - 用于显示资产总览 /// /// 设计规则 ("The Kinetic Vault"): /// - 渐变背景: Neon Blue → Electric Purple /// - 圆角: xl (16px) /// - 无边框,使用渐变层次 /// - 微妙阴影: 10% black, blur 10px class AssetCard extends StatelessWidget { final String title; final String balance; final String? subtitle; final String? profit; final bool? isProfit; final List? items; final Gradient? gradient; final VoidCallback? onTap; /// 默认渐变色 - Neon Blue → Electric Purple static LinearGradient defaultGradientBuilder(bool isDark) => LinearGradient( colors: isDark ? [AppColorScheme.darkPrimary, AppColorScheme.darkSecondary] : [AppColorScheme.lightPrimary, AppColorScheme.lightSecondary], begin: Alignment.topLeft, end: Alignment.bottomRight, ); /// 翡翠渐变 - 用于盈利展示(主题感知) static LinearGradient getEmeraldGradient(bool isDark) => LinearGradient( colors: isDark ? [AppColorScheme.darkTertiary, const Color(0xFF7de8b8)] : [AppColorScheme.lightTertiary, const Color(0xFF00c987)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); const AssetCard({ super.key, this.title = '总资产', required this.balance, this.subtitle, this.profit, this.isProfit, this.items, this.gradient, this.onTap, }); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); final colorScheme = Theme.of(context).colorScheme; final isDark = Theme.of(context).brightness == Brightness.dark; final cardGradient = gradient ?? defaultGradientBuilder(isDark); // 主题感知颜色 - 在渐变背景上使用 onPrimary final primaryTextColor = colorScheme.onPrimary; final secondaryTextColor = colorScheme.onPrimary.withValues(alpha: 0.7); return GestureDetector( onTap: onTap, child: Container( width: double.infinity, padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( gradient: cardGradient, borderRadius: BorderRadius.circular(AppRadius.xl), boxShadow: [ BoxShadow( color: colorScheme.onSurface.withValues(alpha: 0.1), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题行 Row( children: [ Text( title, style: theme.textTheme.small.copyWith(color: secondaryTextColor), ), const Spacer(), if (onTap != null) Icon( LucideIcons.chevronRight, color: secondaryTextColor, size: 18, ), ], ), const SizedBox(height: AppSpacing.sm), // 余额 - 大标题 Text( balance, style: theme.textTheme.h1.copyWith( fontWeight: FontWeight.bold, color: primaryTextColor, fontSize: 32, ), ), // 盈亏信息 if (profit != null) ...[ const SizedBox(height: AppSpacing.md), Row( children: [ Icon( isProfit == true ? LucideIcons.trendingUp : LucideIcons.trendingDown, color: secondaryTextColor, size: 16, ), const SizedBox(width: 6), Text( '盈亏: $profit', style: theme.textTheme.small.copyWith(color: secondaryTextColor), ), ], ), ], // 子项 if (items != null && items!.isNotEmpty) ...[ const SizedBox(height: AppSpacing.lg), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: items!.map((item) => _buildItem(item, primaryTextColor, secondaryTextColor)).toList(), ), ], ], ), ), ).animate().fadeIn(duration: 400.ms).slideY(begin: 0.1, end: 0); } Widget _buildItem(AssetItem item, Color primaryTextColor, Color secondaryTextColor) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.label, style: TextStyle(fontSize: 12, color: secondaryTextColor), ), const SizedBox(height: 4), Text( item.value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: primaryTextColor, ), ), ], ); } } /// 资产子项 class AssetItem { final String label; final String value; const AssetItem({ required this.label, required this.value, }); } /// 简洁资产卡片 - 用于列表中显示 /// /// 设计规则: /// - 使用 surface 层次而非边框 /// - 圆角: xl (16px) /// - 涨跌标签: 15% 透明度背景 class AssetCardCompact extends StatelessWidget { final String title; final String balance; final String? change; final bool? isUp; final VoidCallback? onTap; const AssetCardCompact({ super.key, required this.title, required this.balance, this.change, this.isUp, this.onTap, }); @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); final isValueUp = isUp ?? true; return ShadCard( padding: const EdgeInsets.all(AppSpacing.md), child: InkWell( onTap: onTap, child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.muted, ), const SizedBox(height: 4), Text( balance, style: theme.textTheme.h3.copyWith( fontWeight: FontWeight.bold, ), ), ], ), ), if (change != null) Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: getChangeBackgroundColor(isValueUp), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Text( change!, style: TextStyle( color: getChangeColor(isValueUp), fontWeight: FontWeight.w600, ), ), ), ], ), ), ); } }