import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../theme/app_theme.dart'; import '../badges/count_badge.dart'; enum IconButtonVariant { standard, filled, outlined, ghost, gradient, glass, } enum IconButtonShape { circle, rounded, square, } class SophisticatedIconButton extends StatefulWidget { final IconData icon; final VoidCallback? onPressed; final VoidCallback? onLongPress; final IconButtonVariant variant; final IconButtonShape shape; final double? size; final Color? backgroundColor; final Color? foregroundColor; final Color? borderColor; final Gradient? gradient; final bool animated; final bool disabled; final String? tooltip; final Widget? badge; final int? notificationCount; final bool showPulse; const SophisticatedIconButton({ super.key, required this.icon, this.onPressed, this.onLongPress, this.variant = IconButtonVariant.standard, this.shape = IconButtonShape.circle, this.size, this.backgroundColor, this.foregroundColor, this.borderColor, this.gradient, this.animated = true, this.disabled = false, this.tooltip, this.badge, this.notificationCount, this.showPulse = false, }); @override State createState() => _SophisticatedIconButtonState(); } class _SophisticatedIconButtonState extends State with TickerProviderStateMixin { late AnimationController _pressController; late AnimationController _pulseController; late AnimationController _rotationController; late Animation _scaleAnimation; late Animation _pulseAnimation; late Animation _rotationAnimation; @override void initState() { super.initState(); _pressController = AnimationController( duration: const Duration(milliseconds: 100), vsync: this, ); _pulseController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _rotationController = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.9, ).animate(CurvedAnimation( parent: _pressController, curve: Curves.easeInOut, )); _pulseAnimation = Tween( begin: 1.0, end: 1.1, ).animate(CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, )); _rotationAnimation = Tween( begin: 0.0, end: 0.25, ).animate(CurvedAnimation( parent: _rotationController, curve: Curves.elasticOut, )); if (widget.showPulse) { _pulseController.repeat(reverse: true); } } @override void dispose() { _pressController.dispose(); _pulseController.dispose(); _rotationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final config = _getButtonConfig(); final buttonSize = widget.size ?? 48.0; final iconSize = buttonSize * 0.5; Widget button = AnimatedBuilder( animation: Listenable.merge([_pressController, _pulseController, _rotationController]), builder: (context, child) { return Transform.scale( scale: widget.animated ? _scaleAnimation.value * (widget.showPulse ? _pulseAnimation.value : 1.0) : 1.0, child: Transform.rotate( angle: widget.animated ? _rotationAnimation.value : 0.0, child: Container( width: buttonSize, height: buttonSize, decoration: _getDecoration(config, buttonSize), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.disabled ? null : _handleTap, onLongPress: widget.disabled ? null : widget.onLongPress, onTapDown: widget.animated && !widget.disabled ? (_) => _pressController.forward() : null, onTapUp: widget.animated && !widget.disabled ? (_) => _pressController.reverse() : null, onTapCancel: widget.animated && !widget.disabled ? () => _pressController.reverse() : null, customBorder: _getInkWellBorder(buttonSize), child: Center( child: Icon( widget.icon, size: iconSize, color: widget.disabled ? AppTheme.textHint : config.foregroundColor, ), ), ), ), ), ), ); }, ); // Add badge if provided if (widget.badge != null || widget.notificationCount != null) { button = Stack( clipBehavior: Clip.none, children: [ button, if (widget.notificationCount != null) Positioned( top: -8, right: -8, child: CountBadge( count: widget.notificationCount!, size: 18, ), ), if (widget.badge != null) Positioned( top: -4, right: -4, child: widget.badge!, ), ], ); } if (widget.tooltip != null) { button = Tooltip( message: widget.tooltip!, child: button, ); } return button; } _IconButtonConfig _getButtonConfig() { switch (widget.variant) { case IconButtonVariant.standard: return _IconButtonConfig( backgroundColor: Colors.transparent, foregroundColor: widget.foregroundColor ?? AppTheme.textPrimary, hasElevation: false, ); case IconButtonVariant.filled: return _IconButtonConfig( backgroundColor: widget.backgroundColor ?? AppTheme.primaryColor, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, ); case IconButtonVariant.outlined: return _IconButtonConfig( backgroundColor: Colors.transparent, foregroundColor: widget.foregroundColor ?? AppTheme.primaryColor, borderColor: widget.borderColor ?? AppTheme.primaryColor, hasElevation: false, ); case IconButtonVariant.ghost: return _IconButtonConfig( backgroundColor: (widget.backgroundColor ?? AppTheme.primaryColor).withOpacity(0.1), foregroundColor: widget.foregroundColor ?? AppTheme.primaryColor, hasElevation: false, ); case IconButtonVariant.gradient: return _IconButtonConfig( backgroundColor: Colors.transparent, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, useGradient: true, ); case IconButtonVariant.glass: return _IconButtonConfig( backgroundColor: Colors.white.withOpacity(0.2), foregroundColor: widget.foregroundColor ?? AppTheme.textPrimary, borderColor: Colors.white.withOpacity(0.3), hasElevation: true, isGlass: true, ); } } Decoration _getDecoration(_IconButtonConfig config, double size) { final borderRadius = _getBorderRadius(size); if (config.useGradient) { return BoxDecoration( gradient: widget.gradient ?? LinearGradient( colors: [ widget.backgroundColor ?? AppTheme.primaryColor, (widget.backgroundColor ?? AppTheme.primaryColor).withOpacity(0.7), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: borderRadius, boxShadow: config.hasElevation ? _getShadow(config, size) : null, ); } return BoxDecoration( color: config.backgroundColor, borderRadius: borderRadius, border: config.borderColor != null ? Border.all(color: config.borderColor!, width: 1.5) : null, boxShadow: config.hasElevation && !widget.disabled ? _getShadow(config, size) : null, ); } BorderRadius _getBorderRadius(double size) { switch (widget.shape) { case IconButtonShape.circle: return BorderRadius.circular(size / 2); case IconButtonShape.rounded: return BorderRadius.circular(size * 0.25); case IconButtonShape.square: return BorderRadius.circular(8); } } ShapeBorder _getInkWellBorder(double size) { switch (widget.shape) { case IconButtonShape.circle: return const CircleBorder(); case IconButtonShape.rounded: return RoundedRectangleBorder( borderRadius: BorderRadius.circular(size * 0.25), ); case IconButtonShape.square: return RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ); } } List _getShadow(_IconButtonConfig config, double size) { final shadowColor = config.useGradient ? (widget.backgroundColor ?? AppTheme.primaryColor) : config.backgroundColor; return [ BoxShadow( color: shadowColor.withOpacity(0.3), blurRadius: size * 0.3, offset: Offset(0, size * 0.1), ), ]; } void _handleTap() { HapticFeedback.selectionClick(); if (widget.animated) { _rotationController.forward().then((_) { _rotationController.reverse(); }); } widget.onPressed?.call(); } } class _IconButtonConfig { final Color backgroundColor; final Color foregroundColor; final Color? borderColor; final bool hasElevation; final bool useGradient; final bool isGlass; _IconButtonConfig({ required this.backgroundColor, required this.foregroundColor, this.borderColor, this.hasElevation = false, this.useGradient = false, this.isGlass = false, }); }