import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../theme/app_theme.dart'; enum FABVariant { primary, secondary, gradient, glass, morphing, } enum FABSize { small, regular, large, extended, } class SophisticatedFAB extends StatefulWidget { final IconData? icon; final String? label; final VoidCallback? onPressed; final FABVariant variant; final FABSize size; final Color? backgroundColor; final Color? foregroundColor; final Gradient? gradient; final bool animated; final bool showPulse; final List? morphingIcons; final Duration morphingDuration; final String? tooltip; const SophisticatedFAB({ super.key, this.icon, this.label, this.onPressed, this.variant = FABVariant.primary, this.size = FABSize.regular, this.backgroundColor, this.foregroundColor, this.gradient, this.animated = true, this.showPulse = false, this.morphingIcons, this.morphingDuration = const Duration(seconds: 2), this.tooltip, }); @override State createState() => _SophisticatedFABState(); } class _SophisticatedFABState extends State with TickerProviderStateMixin { late AnimationController _scaleController; late AnimationController _rotationController; late AnimationController _pulseController; late AnimationController _morphingController; late Animation _scaleAnimation; late Animation _rotationAnimation; late Animation _pulseAnimation; int _currentMorphingIndex = 0; @override void initState() { super.initState(); _scaleController = AnimationController( duration: const Duration(milliseconds: 150), vsync: this, ); _rotationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _pulseController = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); _morphingController = AnimationController( duration: widget.morphingDuration, vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.9, ).animate(CurvedAnimation( parent: _scaleController, curve: Curves.easeInOut, )); _rotationAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _rotationController, curve: Curves.elasticOut, )); _pulseAnimation = Tween( begin: 1.0, end: 1.2, ).animate(CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, )); if (widget.showPulse) { _pulseController.repeat(reverse: true); } if (widget.morphingIcons != null && widget.morphingIcons!.isNotEmpty) { _startMorphing(); } } void _startMorphing() { _morphingController.addListener(() { if (_morphingController.isCompleted) { setState(() { _currentMorphingIndex = (_currentMorphingIndex + 1) % widget.morphingIcons!.length; }); _morphingController.reset(); _morphingController.forward(); } }); _morphingController.forward(); } @override void dispose() { _scaleController.dispose(); _rotationController.dispose(); _pulseController.dispose(); _morphingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final config = _getFABConfig(); Widget fab = AnimatedBuilder( animation: Listenable.merge([ _scaleController, _rotationController, _pulseController, ]), 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.1 : 0.0, child: Container( width: _getSize(), height: _getSize(), decoration: _getDecoration(config), child: Material( color: Colors.transparent, child: InkWell( onTap: _handleTap, onTapDown: widget.animated ? (_) => _scaleController.forward() : null, onTapUp: widget.animated ? (_) => _scaleController.reverse() : null, onTapCancel: widget.animated ? () => _scaleController.reverse() : null, customBorder: const CircleBorder(), child: _buildContent(config), ), ), ), ), ); }, ); if (widget.tooltip != null) { fab = Tooltip( message: widget.tooltip!, child: fab, ); } return fab; } Widget _buildContent(_FABConfig config) { if (widget.size == FABSize.extended && widget.label != null) { return _buildExtendedContent(config); } return Center( child: _buildIcon(config), ); } Widget _buildExtendedContent(_FABConfig config) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildIcon(config), const SizedBox(width: 8), Text( widget.label!, style: TextStyle( color: config.foregroundColor, fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ); } Widget _buildIcon(_FABConfig config) { IconData iconToShow = widget.icon ?? Icons.add; if (widget.morphingIcons != null && widget.morphingIcons!.isNotEmpty) { iconToShow = widget.morphingIcons![_currentMorphingIndex]; } return AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { return ScaleTransition( scale: animation, child: RotationTransition( turns: animation, child: child, ), ); }, child: Icon( iconToShow, key: ValueKey(iconToShow), color: config.foregroundColor, size: _getIconSize(), ), ); } _FABConfig _getFABConfig() { switch (widget.variant) { case FABVariant.primary: return _FABConfig( backgroundColor: widget.backgroundColor ?? AppTheme.primaryColor, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, ); case FABVariant.secondary: return _FABConfig( backgroundColor: widget.backgroundColor ?? AppTheme.secondaryColor, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, ); case FABVariant.gradient: return _FABConfig( backgroundColor: Colors.transparent, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, useGradient: true, ); case FABVariant.glass: return _FABConfig( backgroundColor: Colors.white.withOpacity(0.2), foregroundColor: widget.foregroundColor ?? AppTheme.textPrimary, borderColor: Colors.white.withOpacity(0.3), hasElevation: true, isGlass: true, ); case FABVariant.morphing: return _FABConfig( backgroundColor: widget.backgroundColor ?? AppTheme.accentColor, foregroundColor: widget.foregroundColor ?? Colors.white, hasElevation: true, isMorphing: true, ); } } Decoration _getDecoration(_FABConfig config) { 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, ), shape: BoxShape.circle, boxShadow: config.hasElevation ? _getShadow(config) : null, ); } return BoxDecoration( color: config.backgroundColor, shape: BoxShape.circle, border: config.borderColor != null ? Border.all(color: config.borderColor!, width: 1) : null, boxShadow: config.hasElevation ? _getShadow(config) : null, ); } List _getShadow(_FABConfig config) { final shadowColor = config.useGradient ? (widget.backgroundColor ?? AppTheme.primaryColor) : config.backgroundColor; return [ BoxShadow( color: shadowColor.withOpacity(0.4), blurRadius: 20, offset: const Offset(0, 8), ), BoxShadow( color: shadowColor.withOpacity(0.2), blurRadius: 40, offset: const Offset(0, 16), ), ]; } double _getSize() { switch (widget.size) { case FABSize.small: return 40; case FABSize.regular: return 56; case FABSize.large: return 72; case FABSize.extended: return 56; // Height for extended FAB } } double _getIconSize() { switch (widget.size) { case FABSize.small: return 20; case FABSize.regular: return 24; case FABSize.large: return 32; case FABSize.extended: return 24; } } void _handleTap() { HapticFeedback.lightImpact(); if (widget.animated) { _rotationController.forward().then((_) { _rotationController.reverse(); }); } widget.onPressed?.call(); } } class _FABConfig { final Color backgroundColor; final Color foregroundColor; final Color? borderColor; final bool hasElevation; final bool useGradient; final bool isGlass; final bool isMorphing; _FABConfig({ required this.backgroundColor, required this.foregroundColor, this.borderColor, this.hasElevation = false, this.useGradient = false, this.isGlass = false, this.isMorphing = false, }); }