import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../theme/app_theme.dart'; class LoadingButton extends StatefulWidget { final VoidCallback? onPressed; final String text; final bool isLoading; final double? width; final double height; final Color? backgroundColor; final Color? textColor; final IconData? icon; final bool enabled; const LoadingButton({ super.key, required this.onPressed, required this.text, this.isLoading = false, this.width, this.height = 48, this.backgroundColor, this.textColor, this.icon, this.enabled = true, }); @override State createState() => _LoadingButtonState(); } class _LoadingButtonState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _scaleAnimation; late Animation _opacityAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 150), vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.95, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); _opacityAnimation = Tween( begin: 1.0, end: 0.8, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); } @override void dispose() { _animationController.dispose(); super.dispose(); } bool get _isEnabled => widget.enabled && !widget.isLoading && widget.onPressed != null; Color get _backgroundColor { if (!_isEnabled) { return AppTheme.textHint.withOpacity(0.3); } return widget.backgroundColor ?? AppTheme.primaryColor; } Color get _textColor { if (!_isEnabled) { return AppTheme.textHint; } return widget.textColor ?? Colors.white; } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animationController, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Opacity( opacity: _opacityAnimation.value, child: Container( width: widget.width, height: widget.height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), boxShadow: _isEnabled ? [ BoxShadow( color: _backgroundColor.withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 4), ), ] : null, ), child: ElevatedButton( onPressed: _isEnabled ? _handlePressed : null, style: ElevatedButton.styleFrom( backgroundColor: _backgroundColor, foregroundColor: _textColor, elevation: 0, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 24), ), child: _buildButtonContent(), ), ), ), ); }, ); } Widget _buildButtonContent() { if (widget.isLoading) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 20, height: 20, child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(_textColor), strokeWidth: 2, ), ), const SizedBox(width: 12), Text( 'Chargement...', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: _textColor, ), ), ], ); } if (widget.icon != null) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( widget.icon, size: 20, color: _textColor, ), const SizedBox(width: 8), Text( widget.text, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: _textColor, ), ), ], ); } return Text( widget.text, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: _textColor, ), ); } void _handlePressed() { if (!_isEnabled) return; // Animation de pression _animationController.forward().then((_) { _animationController.reverse(); }); // Vibration tactile HapticFeedback.lightImpact(); // Callback widget.onPressed?.call(); } }