Files
monisuo/flutter_monisuo/lib/ui/components/gradient_button.dart

230 lines
6.4 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import '../../core/theme/app_color_scheme.dart';
import '../../core/theme/app_spacing.dart';
/// 渐变按钮组件 - 支持 CTA 渐变效果
///
/// 设计规则:
/// - 渐变方向: 135度 (primary → primary_container)
/// - 圆角: xxl (24px / 1.5rem)
/// - 无边框
class GradientButton extends StatelessWidget {
final String text;
final VoidCallback? onPressed;
final bool isLoading;
final bool isFullWidth;
final LinearGradient? gradient;
final IconData? icon;
final double height;
const GradientButton({
super.key,
required this.text,
this.onPressed,
this.isLoading = false,
this.isFullWidth = true,
this.gradient,
this.icon,
this.height = 48,
});
/// CTA 按钮 - 使用主题渐变
factory GradientButton.cta({
Key? key,
required String text,
VoidCallback? onPressed,
bool isLoading = false,
bool isFullWidth = true,
IconData? icon,
bool isDark = true,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon,
gradient: isDark ? AppColorScheme.darkCtaGradient : AppColorScheme.lightCtaGradient,
);
}
/// 买入按钮 - 翡翠绿渐变
factory GradientButton.buy({
Key? key,
required String text,
VoidCallback? onPressed,
bool isLoading = false,
bool isFullWidth = true,
IconData? icon,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon ?? Icons.arrow_downward_rounded,
gradient: AppColorScheme.buyGradient,
);
}
/// 卖出按钮 - 红色渐变
factory GradientButton.sell({
Key? key,
required String text,
VoidCallback? onPressed,
bool isLoading = false,
bool isFullWidth = true,
IconData? icon,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon ?? Icons.arrow_upward_rounded,
gradient: AppColorScheme.sellGradient,
);
}
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final colorScheme = Theme.of(context).colorScheme;
final buttonGradient = gradient ??
(isDark ? AppColorScheme.darkCtaGradient : AppColorScheme.lightCtaGradient);
// 主题感知颜色 - 在渐变背景上使用 onPrimary
final textColor = colorScheme.onPrimary;
return Container(
width: isFullWidth ? double.infinity : null,
height: height,
decoration: BoxDecoration(
gradient: buttonGradient,
borderRadius: BorderRadius.circular(AppRadius.xxl),
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: isLoading ? null : onPressed,
borderRadius: BorderRadius.circular(AppRadius.xxl),
child: Center(
child: Row(
mainAxisSize: isFullWidth ? MainAxisSize.max : MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isLoading)
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: textColor,
),
)
else ...[
if (icon != null) ...[
Icon(icon, size: 18, color: textColor),
SizedBox(width: AppSpacing.sm),
],
Text(
text,
style: TextStyle(
color: textColor,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
],
),
),
),
),
);
}
}
/// Ghost 按钮 - 次要操作
///
/// 设计规则:
/// - Ghost Border: 15% opacity outline-variant
/// - 圆角: xxl (24px)
/// - 主色文字
class GhostButton extends StatelessWidget {
final String text;
final VoidCallback? onPressed;
final bool isLoading;
final bool isFullWidth;
final IconData? icon;
const GhostButton({
super.key,
required this.text,
this.onPressed,
this.isLoading = false,
this.isFullWidth = true,
this.icon,
});
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final textColor = isDark ? AppColorScheme.darkPrimary : AppColorScheme.lightPrimary;
final borderColor = isDark
? AppColorScheme.darkOutlineVariant.withValues(alpha: 0.15)
: AppColorScheme.lightOutlineVariant.withValues(alpha: 0.3);
return Container(
width: isFullWidth ? double.infinity : null,
height: 48,
decoration: BoxDecoration(
color: Colors.transparent,
border: Border.all(color: borderColor, width: 1),
borderRadius: BorderRadius.circular(AppRadius.xxl),
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: isLoading ? null : onPressed,
borderRadius: BorderRadius.circular(AppRadius.xxl),
child: Center(
child: Row(
mainAxisSize: isFullWidth ? MainAxisSize.max : MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isLoading)
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: textColor,
),
)
else ...[
if (icon != null) ...[
Icon(icon, size: 18, color: textColor),
SizedBox(width: AppSpacing.sm),
],
Text(
text,
style: TextStyle(
color: textColor,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
],
),
),
),
),
);
}
}