import 'package:flutter/material.dart'; import '../../../../shared/theme/app_theme.dart'; import '../../../../shared/theme/design_system.dart'; /// Grille de statistiques compacte pour mobile class StatsGridCard extends StatefulWidget { const StatsGridCard({ super.key, required this.stats, this.crossAxisCount = 2, this.childAspectRatio = 1.2, }); final Map stats; final int crossAxisCount; final double childAspectRatio; @override State createState() => _StatsGridCardState(); } class _StatsGridCardState extends State with TickerProviderStateMixin { late List _animationControllers; late List> _scaleAnimations; late List> _slideAnimations; @override void initState() { super.initState(); _initializeAnimations(); } void _initializeAnimations() { const itemCount = 4; // Nombre de statistiques _animationControllers = List.generate( itemCount, (index) => AnimationController( duration: Duration( milliseconds: DesignSystem.animationMedium.inMilliseconds + (index * 100), ), vsync: this, ), ); _scaleAnimations = _animationControllers.map((controller) { return Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: controller, curve: DesignSystem.animationCurveEnter, )); }).toList(); _slideAnimations = _animationControllers.map((controller) { return Tween( begin: const Offset(0, 0.5), end: Offset.zero, ).animate(CurvedAnimation( parent: controller, curve: DesignSystem.animationCurveEnter, )); }).toList(); // Démarrer les animations en cascade for (int i = 0; i < _animationControllers.length; i++) { Future.delayed(Duration(milliseconds: i * 100), () { if (mounted) { _animationControllers[i].forward(); } }); } } @override void dispose() { for (final controller in _animationControllers) { controller.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { final statsItems = [ _StatItem( title: 'Total Membres', value: widget.stats['totalMembres'].toString(), icon: Icons.people, color: AppTheme.primaryColor, trend: '+${widget.stats['nouveauxCeMois']}', trendPositive: true, ), _StatItem( title: 'Membres Actifs', value: widget.stats['membresActifs'].toString(), icon: Icons.person, color: AppTheme.successColor, trend: '${widget.stats['tauxActivite']}%', trendPositive: widget.stats['tauxActivite'] >= 70, ), _StatItem( title: 'Nouveaux ce mois', value: widget.stats['nouveauxCeMois'].toString(), icon: Icons.person_add, color: AppTheme.infoColor, trend: 'Ce mois', trendPositive: widget.stats['nouveauxCeMois'] > 0, ), _StatItem( title: 'Taux d\'activité', value: '${widget.stats['tauxActivite']}%', icon: Icons.trending_up, color: AppTheme.warningColor, trend: widget.stats['tauxActivite'] >= 70 ? 'Excellent' : 'Moyen', trendPositive: widget.stats['tauxActivite'] >= 70, ), ]; return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: widget.crossAxisCount, childAspectRatio: widget.childAspectRatio, crossAxisSpacing: DesignSystem.spacingMd, mainAxisSpacing: DesignSystem.spacingMd, ), itemCount: statsItems.length, itemBuilder: (context, index) { return AnimatedBuilder( animation: _animationControllers[index], builder: (context, child) { return SlideTransition( position: _slideAnimations[index], child: ScaleTransition( scale: _scaleAnimations[index], child: _buildStatCard(statsItems[index]), ), ); }, ); }, ); } Widget _buildStatCard(_StatItem item) { return Container( padding: const EdgeInsets.all(DesignSystem.spacingMd), decoration: BoxDecoration( color: AppTheme.surfaceLight, borderRadius: BorderRadius.circular(DesignSystem.radiusLg), boxShadow: DesignSystem.shadowCard, border: Border.all( color: item.color.withOpacity(0.1), width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( padding: const EdgeInsets.all(DesignSystem.spacingSm), decoration: BoxDecoration( color: item.color.withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radiusMd), ), child: Icon( item.icon, color: item.color, size: 20, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: DesignSystem.spacingXs, vertical: 2, ), decoration: BoxDecoration( color: item.trendPositive ? AppTheme.successColor.withOpacity(0.1) : AppTheme.errorColor.withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radiusSm), ), child: Text( item.trend, style: DesignSystem.labelSmall.copyWith( color: item.trendPositive ? AppTheme.successColor : AppTheme.errorColor, fontWeight: FontWeight.w600, fontSize: 10, ), ), ), ], ), const SizedBox(height: DesignSystem.spacingSm), Text( item.value, style: DesignSystem.headlineMedium.copyWith( fontWeight: FontWeight.w800, color: AppTheme.textPrimary, ), ), const SizedBox(height: DesignSystem.spacingXs), Text( item.title, style: DesignSystem.labelMedium.copyWith( color: AppTheme.textSecondary, fontWeight: FontWeight.w500, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ); } } /// Modèle pour un élément de statistique class _StatItem { const _StatItem({ required this.title, required this.value, required this.icon, required this.color, required this.trend, required this.trendPositive, }); final String title; final String value; final IconData icon; final Color color; final String trend; final bool trendPositive; }