224 lines
5.9 KiB
Dart
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,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|