import 'package:flutter/material.dart'; /// 自定义卡片组件 class CustomCard extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final Color? color; final double? elevation; final BorderRadius? borderRadius; final Border? border; final VoidCallback? onTap; final bool isSelected; final Color? selectedColor; final double? width; final double? height; final BoxShadow? shadow; const CustomCard({ super.key, required this.child, this.padding, this.margin, this.color, this.elevation, this.borderRadius, this.border, this.onTap, this.isSelected = false, this.selectedColor, this.width, this.height, this.shadow, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); final defaultBorderRadius = BorderRadius.circular(12); Widget card = Container( width: width, height: height, margin: margin, decoration: BoxDecoration( color: isSelected ? (selectedColor ?? theme.colorScheme.primaryContainer) : (color ?? theme.colorScheme.surface), borderRadius: borderRadius ?? defaultBorderRadius, border: border ?? (isSelected ? Border.all( color: theme.colorScheme.primary, width: 2, ) : Border.all( color: theme.colorScheme.outline.withOpacity(0.2), width: 1, )), boxShadow: shadow != null ? [shadow!] : [ BoxShadow( color: theme.colorScheme.shadow.withOpacity(0.1), blurRadius: elevation ?? 4, offset: const Offset(0, 2), ), ], ), child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: borderRadius ?? defaultBorderRadius, child: Padding( padding: padding ?? const EdgeInsets.all(16), child: child, ), ), ), ); return card; } } /// 信息卡片 class InfoCard extends StatelessWidget { final String title; final String? subtitle; final Widget? leading; final Widget? trailing; final VoidCallback? onTap; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final Color? backgroundColor; final bool isSelected; const InfoCard({ super.key, required this.title, this.subtitle, this.leading, this.trailing, this.onTap, this.padding, this.margin, this.backgroundColor, this.isSelected = false, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return CustomCard( onTap: onTap, padding: padding ?? const EdgeInsets.all(16), margin: margin, color: backgroundColor, isSelected: isSelected, child: Row( children: [ if (leading != null) ...[ leading!, const SizedBox(width: 12), ], Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: isSelected ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurface, ), ), if (subtitle != null) ...[ const SizedBox(height: 4), Text( subtitle!, style: theme.textTheme.bodyMedium?.copyWith( color: isSelected ? theme.colorScheme.onPrimaryContainer.withOpacity(0.8) : theme.colorScheme.onSurface.withOpacity(0.7), ), ), ], ], ), ), if (trailing != null) ...[ const SizedBox(width: 12), trailing!, ], ], ), ); } } /// 统计卡片 class StatCard extends StatelessWidget { final String title; final String value; final String? unit; final IconData? icon; final Color? iconColor; final Color? backgroundColor; final VoidCallback? onTap; final EdgeInsetsGeometry? margin; final Widget? trailing; const StatCard({ super.key, required this.title, required this.value, this.unit, this.icon, this.iconColor, this.backgroundColor, this.onTap, this.margin, this.trailing, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return CustomCard( onTap: onTap, margin: margin, color: backgroundColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ if (icon != null) ...[ Icon( icon, size: 20, color: iconColor ?? theme.colorScheme.primary, ), const SizedBox(width: 8), ], Expanded( child: Text( title, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), ), ), if (trailing != null) trailing!, ], ), const SizedBox(height: 8), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( value, style: theme.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, ), ), if (unit != null) ...[ const SizedBox(width: 4), Padding( padding: const EdgeInsets.only(bottom: 2), child: Text( unit!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), ), ], ], ), ], ), ); } } /// 功能卡片 class FeatureCard extends StatelessWidget { final String title; final String? description; final IconData icon; final Color? iconColor; final Color? backgroundColor; final VoidCallback? onTap; final EdgeInsetsGeometry? margin; final bool isEnabled; final Widget? badge; const FeatureCard({ super.key, required this.title, this.description, required this.icon, this.iconColor, this.backgroundColor, this.onTap, this.margin, this.isEnabled = true, this.badge, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return CustomCard( onTap: isEnabled ? onTap : null, margin: margin, color: backgroundColor, child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: (iconColor ?? theme.colorScheme.primary).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( icon, size: 24, color: isEnabled ? (iconColor ?? theme.colorScheme.primary) : theme.colorScheme.onSurface.withOpacity(0.4), ), ), const SizedBox(height: 12), Text( title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: isEnabled ? theme.colorScheme.onSurface : theme.colorScheme.onSurface.withOpacity(0.4), ), ), if (description != null) ...[ const SizedBox(height: 4), Text( description!, style: theme.textTheme.bodySmall?.copyWith( color: isEnabled ? theme.colorScheme.onSurface.withOpacity(0.7) : theme.colorScheme.onSurface.withOpacity(0.4), ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ], ), if (badge != null) Positioned( top: 0, right: 0, child: badge!, ), ], ), ); } } /// 进度卡片 class ProgressCard extends StatelessWidget { final String title; final String? subtitle; final double progress; final String? progressText; final Color? progressColor; final Color? backgroundColor; final VoidCallback? onTap; final EdgeInsetsGeometry? margin; final Widget? trailing; const ProgressCard({ super.key, required this.title, this.subtitle, required this.progress, this.progressText, this.progressColor, this.backgroundColor, this.onTap, this.margin, this.trailing, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); return CustomCard( onTap: onTap, margin: margin, color: backgroundColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), if (subtitle != null) ...[ const SizedBox(height: 2), Text( subtitle!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), ), ], ], ), ), if (trailing != null) ...[ const SizedBox(width: 12), trailing!, ], ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: LinearProgressIndicator( value: progress.clamp(0.0, 1.0), backgroundColor: theme.colorScheme.surfaceVariant, valueColor: AlwaysStoppedAnimation( progressColor ?? theme.colorScheme.primary, ), minHeight: 6, ), ), if (progressText != null) ...[ const SizedBox(width: 12), Text( progressText!, style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, color: theme.colorScheme.onSurface.withOpacity(0.8), ), ), ], ], ), ], ), ); } }