import 'package:flutter/material.dart'; import '../../../../shared/theme/app_theme.dart'; import '../../../../shared/theme/design_system.dart'; /// Card statistique professionnelle avec design basé sur le nombre d'or class DashboardStatCard extends StatefulWidget { const DashboardStatCard({ super.key, required this.title, required this.value, required this.icon, required this.color, this.trend, this.subtitle, this.onTap, this.isLoading = false, }); final String title; final String value; final IconData icon; final Color color; final String? trend; final String? subtitle; final VoidCallback? onTap; final bool isLoading; @override State createState() => _DashboardStatCardState(); } class _DashboardStatCardState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _scaleAnimation; late Animation _fadeAnimation; bool _isHovered = false; @override void initState() { super.initState(); _animationController = AnimationController( duration: DesignSystem.animationMedium, vsync: this, ); _scaleAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: DesignSystem.animationCurveEnter, )); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: DesignSystem.animationCurve, )); _animationController.forward(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animationController, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: FadeTransition( opacity: _fadeAnimation, child: _buildCard(context), ), ); }, ); } Widget _buildCard(BuildContext context) { return MouseRegion( onEnter: (_) => _setHovered(true), onExit: (_) => _setHovered(false), child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( duration: DesignSystem.animationFast, curve: DesignSystem.animationCurve, padding: EdgeInsets.all(DesignSystem.spacingLg), decoration: BoxDecoration( color: AppTheme.surfaceLight, borderRadius: BorderRadius.circular(DesignSystem.radiusLg), boxShadow: _isHovered ? DesignSystem.shadowCardHover : DesignSystem.shadowCard, border: Border.all( color: widget.color.withOpacity(0.1), width: 1, ), ), child: widget.isLoading ? _buildLoadingState() : _buildContent(), ), ), ); } Widget _buildLoadingState() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildShimmer(40, 40, isCircular: true), if (widget.trend != null) _buildShimmer(60, 24, radius: 12), ], ), SizedBox(height: DesignSystem.spacingMd), _buildShimmer(80, 32), SizedBox(height: DesignSystem.spacingSm), _buildShimmer(120, 16), if (widget.subtitle != null) ...[ SizedBox(height: DesignSystem.spacingXs), _buildShimmer(100, 14), ], ], ); } Widget _buildShimmer(double width, double height, {double? radius, bool isCircular = false}) { return Container( width: width, height: height, decoration: BoxDecoration( color: AppTheme.textHint.withOpacity(0.1), borderRadius: isCircular ? BorderRadius.circular(height / 2) : BorderRadius.circular(radius ?? DesignSystem.radiusSm), ), ); } Widget _buildContent() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), SizedBox(height: DesignSystem.goldenHeight(DesignSystem.spacingLg)), _buildValue(), SizedBox(height: DesignSystem.spacingSm), _buildTitle(), if (widget.subtitle != null) ...[ SizedBox(height: DesignSystem.spacingXs), _buildSubtitle(), ], ], ); } Widget _buildHeader() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildIconContainer(), if (widget.trend != null) _buildTrendBadge(), ], ); } Widget _buildIconContainer() { return Container( width: DesignSystem.goldenWidth(32), height: 32, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ widget.color.withOpacity(0.15), widget.color.withOpacity(0.05), ], ), borderRadius: BorderRadius.circular(DesignSystem.radiusMd), border: Border.all( color: widget.color.withOpacity(0.2), width: 1, ), ), child: Icon( widget.icon, color: widget.color, size: 20, ), ); } Widget _buildTrendBadge() { return Container( padding: EdgeInsets.symmetric( horizontal: DesignSystem.spacingSm, vertical: DesignSystem.spacingXs, ), decoration: BoxDecoration( color: _getTrendColor().withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radiusXl), border: Border.all( color: _getTrendColor().withOpacity(0.2), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( _getTrendIcon(), color: _getTrendColor(), size: 14, ), SizedBox(width: DesignSystem.spacing2xs), Text( widget.trend!, style: DesignSystem.labelSmall.copyWith( color: _getTrendColor(), fontWeight: FontWeight.w600, ), ), ], ), ); } Widget _buildValue() { return Text( widget.value, style: DesignSystem.displayMedium.copyWith( color: widget.color, fontWeight: FontWeight.w800, fontSize: 28, ), ); } Widget _buildTitle() { return Text( widget.title, style: DesignSystem.labelLarge.copyWith( color: AppTheme.textSecondary, fontWeight: FontWeight.w500, ), ); } Widget _buildSubtitle() { return Text( widget.subtitle!, style: DesignSystem.labelMedium.copyWith( color: AppTheme.textHint, ), ); } void _setHovered(bool hovered) { if (mounted) { setState(() { _isHovered = hovered; }); } } Color _getTrendColor() { if (widget.trend == null) return AppTheme.textSecondary; if (widget.trend!.startsWith('+')) { return AppTheme.successColor; } else if (widget.trend!.startsWith('-')) { return AppTheme.errorColor; } else { return AppTheme.warningColor; } } IconData _getTrendIcon() { if (widget.trend == null) return Icons.trending_flat; if (widget.trend!.startsWith('+')) { return Icons.trending_up; } else if (widget.trend!.startsWith('-')) { return Icons.trending_down; } else { return Icons.trending_flat; } } }