Files
monisuo/flutter_monisuo/lib/ui/components/gradient_button.dart
2026-04-07 01:05:05 +08:00

224 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import '../../core/theme/app_spacing.dart';
import '../../core/theme/app_theme_extension.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,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon,
);
}
/// 買入按鈕 - 翡翠綠漸變
factory GradientButton.buy({
Key? key,
required String text,
VoidCallback? onPressed,
bool isLoading = false,
bool isFullWidth = true,
IconData? icon,
LinearGradient? gradient,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon ?? Icons.arrow_downward_rounded,
);
}
/// 賣出按鈕 - 紅色漸變
factory GradientButton.sell({
Key? key,
required String text,
VoidCallback? onPressed,
bool isLoading = false,
bool isFullWidth = true,
IconData? icon,
LinearGradient? gradient,
}) {
return GradientButton(
key: key,
text: text,
onPressed: onPressed,
isLoading: isLoading,
isFullWidth: isFullWidth,
icon: icon ?? Icons.arrow_upward_rounded,
);
}
@override
Widget build(BuildContext context) {
final appColors = context.appColors;
final colorScheme = context.colors;
final buttonGradient = gradient ?? appColors.ctaGradient;
// 主題感知顏色 - 在漸變背景上使用 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 textColor = context.colors.primary;
final borderColor = context.appColors.ghostBorder;
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,
),
),
],
],
),
),
),
),
);
}
}