at main 4.4 kB view raw
1import 'package:flutter/material.dart'; 2 3enum AppButtonVariant { primary, secondary, text } 4 5enum AppButtonSize { normal, small } 6 7class AppButton extends StatelessWidget { 8 final String label; 9 final VoidCallback? onPressed; 10 final bool loading; 11 final bool disabled; 12 final AppButtonVariant variant; 13 final AppButtonSize size; 14 final IconData? icon; 15 final double height; 16 final double borderRadius; 17 final double fontSize; 18 final EdgeInsetsGeometry? padding; 19 20 const AppButton({ 21 super.key, 22 required this.label, 23 this.onPressed, 24 this.loading = false, 25 this.disabled = false, 26 this.variant = AppButtonVariant.primary, 27 this.size = AppButtonSize.normal, 28 this.icon, 29 this.height = 44, 30 this.borderRadius = 6, 31 this.fontSize = 16, 32 this.padding, 33 }); 34 35 @override 36 Widget build(BuildContext context) { 37 final theme = Theme.of(context); 38 final Color primaryColor = theme.colorScheme.primary; 39 final Color secondaryColor = theme.colorScheme.surfaceContainerHighest; 40 final Color secondaryText = theme.colorScheme.onSurface; 41 final Color primaryText = theme.colorScheme.onPrimary; 42 final bool isPrimary = variant == AppButtonVariant.primary; 43 final bool isText = variant == AppButtonVariant.text; 44 45 final double resolvedHeight = size == AppButtonSize.small ? 32 : height; 46 final double resolvedFontSize = size == AppButtonSize.small ? 14 : fontSize; 47 final double resolvedBorderRadius = size == AppButtonSize.small ? 5 : borderRadius; 48 final EdgeInsetsGeometry resolvedPadding = 49 padding ?? 50 (size == AppButtonSize.small 51 ? const EdgeInsets.symmetric(horizontal: 14, vertical: 0) 52 : const EdgeInsets.symmetric(horizontal: 16)); 53 54 if (isText) { 55 return SizedBox( 56 height: resolvedHeight, 57 child: TextButton( 58 onPressed: (loading || disabled) ? null : onPressed, 59 style: TextButton.styleFrom( 60 padding: resolvedPadding, 61 foregroundColor: disabled ? secondaryText.withOpacity(0.5) : secondaryText, 62 textStyle: theme.textTheme.labelLarge?.copyWith( 63 fontWeight: FontWeight.w600, 64 fontSize: resolvedFontSize, 65 ), 66 ), 67 child: Text( 68 label, 69 style: theme.textTheme.labelLarge?.copyWith( 70 color: disabled ? primaryColor.withOpacity(0.5) : primaryColor, 71 fontWeight: FontWeight.w600, 72 fontSize: resolvedFontSize, 73 ), 74 ), 75 ), 76 ); 77 } 78 79 return SizedBox( 80 height: resolvedHeight, 81 child: ElevatedButton( 82 onPressed: (loading || disabled) ? null : onPressed, 83 style: ElevatedButton.styleFrom( 84 backgroundColor: isPrimary 85 ? (disabled ? primaryColor.withOpacity(0.5) : primaryColor) 86 : (disabled ? secondaryColor.withOpacity(0.5) : secondaryColor), 87 foregroundColor: isPrimary ? primaryText : secondaryText, 88 elevation: 0, 89 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(resolvedBorderRadius)), 90 padding: resolvedPadding, 91 textStyle: theme.textTheme.labelLarge?.copyWith( 92 fontWeight: FontWeight.w600, 93 fontSize: resolvedFontSize, 94 ), 95 ), 96 child: loading 97 ? SizedBox( 98 width: 22, 99 height: 22, 100 child: CircularProgressIndicator( 101 color: isPrimary ? primaryColor : secondaryColor, 102 strokeWidth: 2, 103 ), 104 ) 105 : Row( 106 mainAxisSize: MainAxisSize.min, 107 mainAxisAlignment: MainAxisAlignment.center, 108 children: [ 109 if (icon != null) ...[ 110 Icon(icon, size: 20, color: isPrimary ? primaryText : secondaryText), 111 const SizedBox(width: 8), 112 ], 113 Text( 114 label, 115 style: theme.textTheme.labelLarge?.copyWith( 116 color: isPrimary ? primaryText : secondaryText, 117 fontWeight: FontWeight.w700, 118 fontSize: resolvedFontSize, 119 ), 120 ), 121 ], 122 ), 123 ), 124 ); 125 } 126}