import 'package:flutter/material.dart'; import 'package:animate_do/animate_do.dart'; /// Svrnty Design System Components /// Reusable, branded components for the Svrnty Console application /// /// Brand Colors: /// - Primary (Crimson): #C44D58 /// - Secondary (Slate Blue): #475C6C // ============================================================================ // SVRNTY BUTTONS // ============================================================================ enum SvrntyButtonVariant { primary, secondary, ghost, danger } class SvrntyButton extends StatelessWidget { final String text; final VoidCallback? onPressed; final SvrntyButtonVariant variant; final IconData? icon; final bool isLoading; final bool fullWidth; const SvrntyButton({ Key? key, required this.text, this.onPressed, this.variant = SvrntyButtonVariant.primary, this.icon, this.isLoading = false, this.fullWidth = false, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; Color backgroundColor; Color textColor; Color? borderColor; switch (variant) { case SvrntyButtonVariant.primary: backgroundColor = colorScheme.primary; textColor = Colors.white; borderColor = null; break; case SvrntyButtonVariant.secondary: backgroundColor = colorScheme.secondary; textColor = Colors.white; borderColor = null; break; case SvrntyButtonVariant.ghost: backgroundColor = Colors.transparent; textColor = colorScheme.primary; borderColor = colorScheme.primary; break; case SvrntyButtonVariant.danger: backgroundColor = colorScheme.error; textColor = Colors.white; borderColor = null; break; } return FadeInUp( duration: const Duration(milliseconds: 300), child: SizedBox( width: fullWidth ? double.infinity : null, child: ElevatedButton( onPressed: isLoading ? null : onPressed, style: ElevatedButton.styleFrom( backgroundColor: backgroundColor, foregroundColor: textColor, elevation: variant == SvrntyButtonVariant.ghost ? 0 : 2, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: borderColor != null ? BorderSide(color: borderColor, width: 2) : BorderSide.none, ), ), child: isLoading ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(textColor), ), ) : Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: 20), const SizedBox(width: 8), ], Text( text, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16, ), ), ], ), ), ), ); } } // ============================================================================ // SVRNTY CARDS // ============================================================================ class SvrntyCard extends StatelessWidget { final Widget child; final Color? accentColor; final VoidCallback? onTap; final bool showBorder; final EdgeInsetsGeometry? padding; const SvrntyCard({ Key? key, required this.child, this.accentColor, this.onTap, this.showBorder = true, this.padding, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; final effectiveAccentColor = accentColor ?? colorScheme.primary; return FadeInUp( duration: const Duration(milliseconds: 400), child: Container( decoration: showBorder ? BoxDecoration( borderRadius: BorderRadius.circular(16), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ effectiveAccentColor.withOpacity(0.15), effectiveAccentColor.withOpacity(0.05), ], ), border: Border.all( color: effectiveAccentColor.withOpacity(0.3), width: 1, ), ) : null, child: Card( elevation: 2, color: showBorder ? Colors.transparent : null, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: Container( decoration: showBorder ? BoxDecoration( border: Border( left: BorderSide( color: effectiveAccentColor, width: 4, ), ), ) : null, child: Padding( padding: padding ?? const EdgeInsets.all(20), child: child, ), ), ), ), ), ); } } // ============================================================================ // SVRNTY BADGES // ============================================================================ enum SvrntyBadgeStatus { success, warning, error, info, neutral } class SvrntyBadge extends StatelessWidget { final String text; final SvrntyBadgeStatus status; final IconData? icon; const SvrntyBadge({ Key? key, required this.text, this.status = SvrntyBadgeStatus.neutral, this.icon, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; Color backgroundColor; Color textColor; switch (status) { case SvrntyBadgeStatus.success: backgroundColor = Colors.green; textColor = Colors.white; break; case SvrntyBadgeStatus.warning: backgroundColor = Colors.orange; textColor = Colors.white; break; case SvrntyBadgeStatus.error: backgroundColor = colorScheme.error; textColor = Colors.white; break; case SvrntyBadgeStatus.info: backgroundColor = colorScheme.primary; textColor = Colors.white; break; case SvrntyBadgeStatus.neutral: backgroundColor = colorScheme.secondary; textColor = Colors.white; break; } return Pulse( duration: const Duration(milliseconds: 1000), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: backgroundColor.withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, color: textColor, size: 16), const SizedBox(width: 6), ], Text( text, style: TextStyle( color: textColor, fontSize: 12, fontWeight: FontWeight.bold, fontFamily: 'Montserrat', ), ), ], ), ), ); } } // ============================================================================ // SVRNTY ICON BUTTONS // ============================================================================ class SvrntyIconButton extends StatelessWidget { final IconData icon; final VoidCallback? onPressed; final Color? backgroundColor; final Color? iconColor; final double size; const SvrntyIconButton({ Key? key, required this.icon, this.onPressed, this.backgroundColor, this.iconColor, this.size = 40, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return FadeIn( child: Container( width: size, height: size, decoration: BoxDecoration( color: backgroundColor ?? colorScheme.primary.withOpacity(0.1), shape: BoxShape.circle, ), child: IconButton( icon: Icon(icon), color: iconColor ?? colorScheme.primary, iconSize: size * 0.5, onPressed: onPressed, ), ), ); } } // ============================================================================ // SVRNTY SECTION HEADER // ============================================================================ class SvrntySectionHeader extends StatelessWidget { final String title; final String? subtitle; final Widget? action; const SvrntySectionHeader({ Key? key, required this.title, this.subtitle, this.action, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return FadeInLeft( duration: const Duration(milliseconds: 400), child: Row( children: [ Container( width: 4, height: 28, decoration: BoxDecoration( color: colorScheme.primary, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: colorScheme.onSurface, fontFamily: 'Montserrat', ), ), if (subtitle != null) ...[ const SizedBox(height: 4), Text( subtitle!, style: TextStyle( fontSize: 14, color: colorScheme.onSurfaceVariant, fontFamily: 'Montserrat', ), ), ], ], ), ), if (action != null) action!, ], ), ); } } // ============================================================================ // SVRNTY INPUT FIELD // ============================================================================ class SvrntyTextField extends StatelessWidget { final String label; final String? hint; final TextEditingController? controller; final IconData? prefixIcon; final bool obscureText; final TextInputType? keyboardType; final String? Function(String?)? validator; const SvrntyTextField({ Key? key, required this.label, this.hint, this.controller, this.prefixIcon, this.obscureText = false, this.keyboardType, this.validator, }) : super(key: key); @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return FadeInUp( duration: const Duration(milliseconds: 350), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: colorScheme.onSurface, fontFamily: 'Montserrat', ), ), const SizedBox(height: 8), TextFormField( controller: controller, obscureText: obscureText, keyboardType: keyboardType, validator: validator, style: const TextStyle(fontFamily: 'Montserrat'), decoration: InputDecoration( hintText: hint, prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null, filled: true, fillColor: colorScheme.surfaceContainerHighest, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: colorScheme.outline.withOpacity(0.3), ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: colorScheme.outline.withOpacity(0.2), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: colorScheme.primary, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: colorScheme.error, width: 2, ), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 16, ), ), ), ], ), ); } }