120 lines
3.9 KiB
Dart
120 lines
3.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// 주요 액션에 사용되는 Primary 버튼
|
|
/// 저장, 추가, 확인 등의 주요 액션에 사용됩니다.
|
|
class PrimaryButton extends StatefulWidget {
|
|
final String text;
|
|
final VoidCallback? onPressed;
|
|
final bool isLoading;
|
|
final IconData? icon;
|
|
final double? width;
|
|
final double height;
|
|
final Color? backgroundColor;
|
|
final Color? foregroundColor;
|
|
final double fontSize;
|
|
final EdgeInsetsGeometry? padding;
|
|
final double borderRadius;
|
|
final bool enableHoverEffect;
|
|
|
|
const PrimaryButton({
|
|
super.key,
|
|
required this.text,
|
|
this.onPressed,
|
|
this.isLoading = false,
|
|
this.icon,
|
|
this.width,
|
|
this.height = 60,
|
|
this.backgroundColor,
|
|
this.foregroundColor,
|
|
this.fontSize = 18,
|
|
this.padding,
|
|
this.borderRadius = 16,
|
|
this.enableHoverEffect = true,
|
|
});
|
|
|
|
@override
|
|
State<PrimaryButton> createState() => _PrimaryButtonState();
|
|
}
|
|
|
|
class _PrimaryButtonState extends State<PrimaryButton> {
|
|
bool _isHovered = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final effectiveBackgroundColor =
|
|
widget.backgroundColor ?? theme.colorScheme.primary;
|
|
final effectiveForegroundColor =
|
|
widget.foregroundColor ?? theme.colorScheme.onPrimary;
|
|
|
|
Widget button = AnimatedContainer(
|
|
duration: const Duration(milliseconds: 200),
|
|
width: widget.width ?? double.infinity,
|
|
height: widget.height,
|
|
transform: widget.enableHoverEffect && _isHovered
|
|
? Matrix4.diagonal3Values(1.02, 1.02, 1.0)
|
|
: Matrix4.identity(),
|
|
child: ElevatedButton(
|
|
onPressed: widget.isLoading ? null : widget.onPressed,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: effectiveBackgroundColor,
|
|
foregroundColor: effectiveForegroundColor,
|
|
// 고정 높이와 텍스트 잘림 방지를 위해 최소 사이즈 지정
|
|
minimumSize: Size.fromHeight(widget.height),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(widget.borderRadius),
|
|
),
|
|
// 컨테이너에서 높이를 관리하므로 수직 패딩은 0으로 두고
|
|
// 수평 여백만 부여하여 작은 높이(예: 48)에서 글자 잘림 방지
|
|
padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: 16),
|
|
elevation: widget.enableHoverEffect && _isHovered ? 2 : 0,
|
|
shadowColor: Colors.black.withValues(alpha: 0.08),
|
|
disabledBackgroundColor:
|
|
effectiveBackgroundColor.withValues(alpha: 0.6),
|
|
),
|
|
child: widget.isLoading
|
|
? SizedBox(
|
|
width: 24,
|
|
height: 24,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2.5,
|
|
color: effectiveForegroundColor,
|
|
),
|
|
)
|
|
: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (widget.icon != null) ...[
|
|
Icon(
|
|
widget.icon,
|
|
color: effectiveForegroundColor,
|
|
size: _isHovered ? 24 : 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
Text(
|
|
widget.text,
|
|
style: TextStyle(
|
|
fontSize: widget.fontSize,
|
|
fontWeight: FontWeight.w600,
|
|
color: effectiveForegroundColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
if (widget.enableHoverEffect) {
|
|
return MouseRegion(
|
|
onEnter: (_) => setState(() => _isHovered = true),
|
|
onExit: (_) => setState(() => _isHovered = false),
|
|
child: button,
|
|
);
|
|
}
|
|
|
|
return button;
|
|
}
|
|
}
|