import 'package:flutter/material.dart'; import '../../../../shared/theme/app_theme.dart'; import '../../../../shared/theme/design_system.dart'; /// Container professionnel pour les graphiques du dashboard avec animations class DashboardChartCard extends StatefulWidget { const DashboardChartCard({ super.key, required this.title, required this.child, this.subtitle, this.actions, this.height, this.isLoading = false, this.onRefresh, this.showBorder = true, }); final String title; final Widget child; final String? subtitle; final List? actions; final double? height; final bool isLoading; final VoidCallback? onRefresh; final bool showBorder; @override State createState() => _DashboardChartCardState(); } class _DashboardChartCardState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _slideAnimation; late Animation _fadeAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: DesignSystem.animationMedium, vsync: this, ); _slideAnimation = Tween( begin: 30.0, end: 0.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.translate( offset: Offset(0, _slideAnimation.value), child: FadeTransition( opacity: _fadeAnimation, child: _buildCard(), ), ); }, ); } Widget _buildCard() { return Container( height: widget.height, padding: EdgeInsets.all(DesignSystem.spacingLg), decoration: BoxDecoration( color: AppTheme.surfaceLight, borderRadius: BorderRadius.circular(DesignSystem.radiusLg), boxShadow: DesignSystem.shadowCard, border: widget.showBorder ? Border.all( color: AppTheme.borderColor.withOpacity(0.5), width: 1, ) : null, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), SizedBox(height: DesignSystem.spacingLg), Expanded( child: widget.isLoading ? _buildLoadingState() : widget.child, ), ], ), ); } Widget _buildHeader() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: DesignSystem.headlineMedium.copyWith( fontSize: 20, fontWeight: FontWeight.w700, ), ), if (widget.subtitle != null) ...[ SizedBox(height: DesignSystem.spacingXs), Text( widget.subtitle!, style: DesignSystem.bodyMedium.copyWith( color: AppTheme.textSecondary, ), ), ], ], ), ), if (widget.actions != null || widget.onRefresh != null) Row( mainAxisSize: MainAxisSize.min, children: [ if (widget.onRefresh != null) _buildRefreshButton(), if (widget.actions != null) ...widget.actions!, ], ), ], ); } Widget _buildRefreshButton() { return Container( margin: EdgeInsets.only(right: DesignSystem.spacingSm), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onRefresh, borderRadius: BorderRadius.circular(DesignSystem.radiusSm), child: Container( padding: EdgeInsets.all(DesignSystem.spacingSm), decoration: BoxDecoration( color: AppTheme.primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radiusSm), ), child: const Icon( Icons.refresh, size: 18, color: AppTheme.primaryColor, ), ), ), ), ); } Widget _buildLoadingState() { return Column( children: [ Expanded( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 40, height: 40, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation( AppTheme.primaryColor.withOpacity(0.7), ), ), ), SizedBox(height: DesignSystem.spacingMd), Text( 'Chargement des données...', style: DesignSystem.bodyMedium.copyWith( color: AppTheme.textSecondary, ), ), ], ), ), ), ], ); } }