feat(ui): 레트로 UI 시스템 추가
- PressStart2P 픽셀 폰트 추가 - RetroColors: 레트로 RPG 스타일 색상 팔레트 - RetroPanel: 픽셀 테두리 패널 위젯 - RetroButton: 레트로 스타일 버튼 - RetroProgressBar: 픽셀 스타일 진행 바 - PixelBorderPainter: 커스텀 테두리 페인터
This commit is contained in:
150
lib/src/shared/widgets/retro_button.dart
Normal file
150
lib/src/shared/widgets/retro_button.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/src/shared/retro_colors.dart';
|
||||
|
||||
/// 레트로 RPG 스타일 버튼
|
||||
/// 8-bit 게임의 눌림 효과를 재현
|
||||
class RetroButton extends StatefulWidget {
|
||||
const RetroButton({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.onPressed,
|
||||
this.isPrimary = true,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
});
|
||||
|
||||
/// 버튼 내부 컨텐츠
|
||||
final Widget child;
|
||||
|
||||
/// 클릭 콜백 (null이면 비활성화)
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// Primary 버튼 여부 (색상 차이)
|
||||
final bool isPrimary;
|
||||
|
||||
/// 내부 패딩
|
||||
final EdgeInsets padding;
|
||||
|
||||
@override
|
||||
State<RetroButton> createState() => _RetroButtonState();
|
||||
}
|
||||
|
||||
class _RetroButtonState extends State<RetroButton> {
|
||||
bool _isPressed = false;
|
||||
|
||||
bool get _isEnabled => widget.onPressed != null;
|
||||
|
||||
Color get _backgroundColor {
|
||||
if (!_isEnabled) return RetroColors.buttonSecondary.withValues(alpha: 0.5);
|
||||
if (_isPressed) {
|
||||
return widget.isPrimary
|
||||
? RetroColors.buttonPrimaryPressed
|
||||
: RetroColors.buttonSecondaryPressed;
|
||||
}
|
||||
return widget.isPrimary
|
||||
? RetroColors.buttonPrimary
|
||||
: RetroColors.buttonSecondary;
|
||||
}
|
||||
|
||||
Color get _borderTopLeft {
|
||||
if (_isPressed) return RetroColors.panelBorderOuter;
|
||||
return RetroColors.panelBorderInner;
|
||||
}
|
||||
|
||||
Color get _borderBottomRight {
|
||||
if (_isPressed) return RetroColors.panelBorderInner;
|
||||
return RetroColors.panelBorderOuter;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTapDown: _isEnabled ? (_) => setState(() => _isPressed = true) : null,
|
||||
onTapUp: _isEnabled ? (_) => setState(() => _isPressed = false) : null,
|
||||
onTapCancel: _isEnabled ? () => setState(() => _isPressed = false) : null,
|
||||
onTap: widget.onPressed,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 50),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
color: _backgroundColor,
|
||||
border: Border(
|
||||
top: BorderSide(color: _borderTopLeft, width: 2),
|
||||
left: BorderSide(color: _borderTopLeft, width: 2),
|
||||
bottom: BorderSide(color: _borderBottomRight, width: 2),
|
||||
right: BorderSide(color: _borderBottomRight, width: 2),
|
||||
),
|
||||
),
|
||||
transform: _isPressed
|
||||
? Matrix4.translationValues(1, 1, 0)
|
||||
: Matrix4.identity(),
|
||||
child: DefaultTextStyle(
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: 10,
|
||||
color: _isEnabled ? RetroColors.textLight : RetroColors.textDisabled,
|
||||
),
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 레트로 텍스트 버튼 (간편 생성용)
|
||||
class RetroTextButton extends StatelessWidget {
|
||||
const RetroTextButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
this.isPrimary = true,
|
||||
this.icon,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
final bool isPrimary;
|
||||
final IconData? icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RetroButton(
|
||||
onPressed: onPressed,
|
||||
isPrimary: isPrimary,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, size: 14, color: RetroColors.textLight),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
Text(text.toUpperCase()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 레트로 아이콘 버튼
|
||||
class RetroIconButton extends StatelessWidget {
|
||||
const RetroIconButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
this.onPressed,
|
||||
this.size = 32,
|
||||
});
|
||||
|
||||
final IconData icon;
|
||||
final VoidCallback? onPressed;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RetroButton(
|
||||
onPressed: onPressed,
|
||||
padding: EdgeInsets.all(size * 0.25),
|
||||
child: Icon(icon, size: size * 0.5, color: RetroColors.textLight),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user