162 lines
4.5 KiB
Dart
162 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../app/theme/app_colors.dart';
|
|
import '../../app/theme/app_typography.dart';
|
|
import '../../app/theme/app_spacing.dart';
|
|
|
|
/// Genex 按钮组件
|
|
///
|
|
/// 统一的按钮样式,支持 primary/secondary/outline/text 4种变体
|
|
class GenexButton extends StatelessWidget {
|
|
final String label;
|
|
final VoidCallback? onPressed;
|
|
final GenexButtonVariant variant;
|
|
final GenexButtonSize size;
|
|
final IconData? icon;
|
|
final bool isLoading;
|
|
final bool fullWidth;
|
|
|
|
const GenexButton({
|
|
super.key,
|
|
required this.label,
|
|
this.onPressed,
|
|
this.variant = GenexButtonVariant.primary,
|
|
this.size = GenexButtonSize.large,
|
|
this.icon,
|
|
this.isLoading = false,
|
|
this.fullWidth = true,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final child = Row(
|
|
mainAxisSize: fullWidth ? MainAxisSize.max : MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
if (isLoading) ...[
|
|
SizedBox(
|
|
width: 18,
|
|
height: 18,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
valueColor: AlwaysStoppedAnimation(_foregroundColor),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
if (icon != null && !isLoading) ...[
|
|
Icon(icon, size: 20),
|
|
const SizedBox(width: 6),
|
|
],
|
|
Text(label),
|
|
],
|
|
);
|
|
|
|
final effectiveOnPressed = isLoading ? null : onPressed;
|
|
|
|
Widget button;
|
|
switch (variant) {
|
|
case GenexButtonVariant.primary:
|
|
button = ElevatedButton(
|
|
onPressed: effectiveOnPressed,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primary,
|
|
foregroundColor: Colors.white,
|
|
disabledBackgroundColor: AppColors.primary.withValues(alpha: 0.4),
|
|
disabledForegroundColor: Colors.white70,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: AppSpacing.borderRadiusMd,
|
|
),
|
|
textStyle: _textStyle,
|
|
),
|
|
child: child,
|
|
);
|
|
|
|
case GenexButtonVariant.secondary:
|
|
button = ElevatedButton(
|
|
onPressed: effectiveOnPressed,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primarySurface,
|
|
foregroundColor: AppColors.primary,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: AppSpacing.borderRadiusMd,
|
|
),
|
|
textStyle: _textStyle,
|
|
),
|
|
child: child,
|
|
);
|
|
|
|
case GenexButtonVariant.outline:
|
|
button = OutlinedButton(
|
|
onPressed: effectiveOnPressed,
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.primary,
|
|
side: const BorderSide(color: AppColors.primary, width: 1.5),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: AppSpacing.borderRadiusMd,
|
|
),
|
|
textStyle: _textStyle,
|
|
),
|
|
child: child,
|
|
);
|
|
|
|
case GenexButtonVariant.text:
|
|
button = TextButton(
|
|
onPressed: effectiveOnPressed,
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: AppColors.primary,
|
|
textStyle: _textStyle,
|
|
),
|
|
child: child,
|
|
);
|
|
}
|
|
|
|
// Use LayoutBuilder to safely handle fullWidth in unbounded contexts (e.g. Row)
|
|
if (fullWidth) {
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) => SizedBox(
|
|
width: constraints.hasBoundedWidth ? double.infinity : null,
|
|
height: _height,
|
|
child: button,
|
|
),
|
|
);
|
|
}
|
|
return SizedBox(height: _height, child: button);
|
|
}
|
|
|
|
double get _height {
|
|
switch (size) {
|
|
case GenexButtonSize.large:
|
|
return AppSpacing.buttonHeight;
|
|
case GenexButtonSize.medium:
|
|
return AppSpacing.buttonHeightSm;
|
|
case GenexButtonSize.small:
|
|
return 32;
|
|
}
|
|
}
|
|
|
|
TextStyle get _textStyle {
|
|
switch (size) {
|
|
case GenexButtonSize.large:
|
|
return AppTypography.labelLarge;
|
|
case GenexButtonSize.medium:
|
|
return AppTypography.labelMedium;
|
|
case GenexButtonSize.small:
|
|
return AppTypography.labelSmall;
|
|
}
|
|
}
|
|
|
|
Color get _foregroundColor {
|
|
switch (variant) {
|
|
case GenexButtonVariant.primary:
|
|
return Colors.white;
|
|
default:
|
|
return AppColors.primary;
|
|
}
|
|
}
|
|
}
|
|
|
|
enum GenexButtonVariant { primary, secondary, outline, text }
|
|
enum GenexButtonSize { large, medium, small }
|