import 'package:flutter/material.dart'; import '../../theme/app_theme.dart'; enum BadgeType { success, warning, error, info, neutral, premium, new_, } enum BadgeSize { small, medium, large, } enum BadgeVariant { filled, outlined, ghost, gradient, } class StatusBadge extends StatelessWidget { final String text; final BadgeType type; final BadgeSize size; final BadgeVariant variant; final IconData? icon; final VoidCallback? onTap; final bool animated; final String? tooltip; final Widget? customIcon; final bool showPulse; const StatusBadge({ super.key, required this.text, this.type = BadgeType.neutral, this.size = BadgeSize.medium, this.variant = BadgeVariant.filled, this.icon, this.onTap, this.animated = true, this.tooltip, this.customIcon, this.showPulse = false, }); @override Widget build(BuildContext context) { final config = _getBadgeConfig(); Widget badge = AnimatedContainer( duration: animated ? const Duration(milliseconds: 200) : Duration.zero, padding: _getPadding(), decoration: _getDecoration(config), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null || customIcon != null) ...[ _buildIcon(config), SizedBox(width: _getIconSpacing()), ], if (showPulse) ...[ _buildPulseIndicator(config.primaryColor), SizedBox(width: _getIconSpacing()), ], Text( text, style: _getTextStyle(config), ), ], ), ); if (onTap != null) { badge = Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(_getBorderRadius()), child: badge, ), ); } if (tooltip != null) { badge = Tooltip( message: tooltip!, child: badge, ); } return badge; } _BadgeConfig _getBadgeConfig() { switch (type) { case BadgeType.success: return _BadgeConfig( primaryColor: AppTheme.successColor, backgroundColor: AppTheme.successColor.withOpacity(0.1), borderColor: AppTheme.successColor.withOpacity(0.3), ); case BadgeType.warning: return _BadgeConfig( primaryColor: AppTheme.warningColor, backgroundColor: AppTheme.warningColor.withOpacity(0.1), borderColor: AppTheme.warningColor.withOpacity(0.3), ); case BadgeType.error: return _BadgeConfig( primaryColor: AppTheme.errorColor, backgroundColor: AppTheme.errorColor.withOpacity(0.1), borderColor: AppTheme.errorColor.withOpacity(0.3), ); case BadgeType.info: return _BadgeConfig( primaryColor: AppTheme.infoColor, backgroundColor: AppTheme.infoColor.withOpacity(0.1), borderColor: AppTheme.infoColor.withOpacity(0.3), ); case BadgeType.premium: return _BadgeConfig( primaryColor: const Color(0xFFFFD700), backgroundColor: const Color(0xFFFFD700).withOpacity(0.1), borderColor: const Color(0xFFFFD700).withOpacity(0.3), ); case BadgeType.new_: return _BadgeConfig( primaryColor: const Color(0xFFFF6B6B), backgroundColor: const Color(0xFFFF6B6B).withOpacity(0.1), borderColor: const Color(0xFFFF6B6B).withOpacity(0.3), ); default: return _BadgeConfig( primaryColor: AppTheme.textSecondary, backgroundColor: AppTheme.textSecondary.withOpacity(0.1), borderColor: AppTheme.textSecondary.withOpacity(0.3), ); } } EdgeInsets _getPadding() { switch (size) { case BadgeSize.small: return const EdgeInsets.symmetric(horizontal: 8, vertical: 2); case BadgeSize.medium: return const EdgeInsets.symmetric(horizontal: 12, vertical: 4); case BadgeSize.large: return const EdgeInsets.symmetric(horizontal: 16, vertical: 8); } } double _getBorderRadius() { switch (size) { case BadgeSize.small: return 12; case BadgeSize.medium: return 16; case BadgeSize.large: return 20; } } double _getFontSize() { switch (size) { case BadgeSize.small: return 10; case BadgeSize.medium: return 12; case BadgeSize.large: return 14; } } double _getIconSize() { switch (size) { case BadgeSize.small: return 12; case BadgeSize.medium: return 14; case BadgeSize.large: return 16; } } double _getIconSpacing() { switch (size) { case BadgeSize.small: return 4; case BadgeSize.medium: return 6; case BadgeSize.large: return 8; } } Decoration _getDecoration(_BadgeConfig config) { switch (variant) { case BadgeVariant.filled: return BoxDecoration( color: config.primaryColor, borderRadius: BorderRadius.circular(_getBorderRadius()), boxShadow: [ BoxShadow( color: config.primaryColor.withOpacity(0.3), blurRadius: 4, offset: const Offset(0, 2), ), ], ); case BadgeVariant.outlined: return BoxDecoration( color: Colors.transparent, border: Border.all(color: config.borderColor, width: 1), borderRadius: BorderRadius.circular(_getBorderRadius()), ); case BadgeVariant.ghost: return BoxDecoration( color: config.backgroundColor, borderRadius: BorderRadius.circular(_getBorderRadius()), ); case BadgeVariant.gradient: return BoxDecoration( gradient: LinearGradient( colors: [ config.primaryColor, config.primaryColor.withOpacity(0.7), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(_getBorderRadius()), boxShadow: [ BoxShadow( color: config.primaryColor.withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 4), ), ], ); } } TextStyle _getTextStyle(_BadgeConfig config) { Color textColor; switch (variant) { case BadgeVariant.filled: case BadgeVariant.gradient: textColor = Colors.white; break; default: textColor = config.primaryColor; } return TextStyle( fontSize: _getFontSize(), fontWeight: FontWeight.w600, color: textColor, letterSpacing: 0.2, ); } Widget _buildIcon(_BadgeConfig config) { Color iconColor; switch (variant) { case BadgeVariant.filled: case BadgeVariant.gradient: iconColor = Colors.white; break; default: iconColor = config.primaryColor; } if (customIcon != null) { return customIcon!; } return Icon( icon, size: _getIconSize(), color: iconColor, ); } Widget _buildPulseIndicator(Color color) { if (!showPulse) { return Container( width: _getIconSize() * 0.6, height: _getIconSize() * 0.6, decoration: BoxDecoration( color: color, shape: BoxShape.circle, ), ); } return _PulseWidget( size: _getIconSize() * 0.6, color: color, ); } } class _BadgeConfig { final Color primaryColor; final Color backgroundColor; final Color borderColor; _BadgeConfig({ required this.primaryColor, required this.backgroundColor, required this.borderColor, }); } // Pulse animation widget class _PulseWidget extends StatefulWidget { final double size; final Color color; const _PulseWidget({ required this.size, required this.color, }); @override State<_PulseWidget> createState() => _PulseWidgetState(); } class _PulseWidgetState extends State<_PulseWidget> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _animation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); _controller.repeat(reverse: true); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.scale( scale: 0.8 + (_animation.value * 0.4), child: Container( width: widget.size, height: widget.size, decoration: BoxDecoration( color: widget.color.withOpacity(1.0 - _animation.value * 0.5), shape: BoxShape.circle, ), ), ); }, ); } } // Extension for easy badge creation extension BadgeBuilder on String { StatusBadge toBadge({ BadgeType type = BadgeType.neutral, BadgeSize size = BadgeSize.medium, BadgeVariant variant = BadgeVariant.filled, IconData? icon, VoidCallback? onTap, }) { return StatusBadge( text: this, type: type, size: size, variant: variant, icon: icon, onTap: onTap, ); } }