feat(ui): 게임 위젯들 레트로 UI 적용

- death_overlay: 사망 화면 레트로 스타일로 재디자인
- help_dialog: RetroDialog 사용으로 통일
- hp_mp_bar: 레트로 프로그레스 바 스타일 적용
- notification_overlay: 레트로 패널 스타일 적용
- statistics_dialog: RetroDialog로 변경
This commit is contained in:
JiWoong Sul
2025-12-30 19:03:52 +09:00
parent af837fde8a
commit 27e05fb3c1
5 changed files with 742 additions and 605 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:askiineverdie/src/core/notification/notification_service.dart';
import 'package:askiineverdie/src/shared/retro_colors.dart';
/// 알림 오버레이 위젯 (Phase 8: 팝업/토스트 알림)
///
@@ -106,7 +107,7 @@ class _NotificationOverlayState extends State<NotificationOverlay>
}
}
/// 알림 카드 위젯
/// 레트로 스타일 알림 카드 위젯
class _NotificationCard extends StatelessWidget {
const _NotificationCard({
required this.notification,
@@ -118,118 +119,191 @@ class _NotificationCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final (bgColor, icon, iconColor) = _getStyleForType(notification.type);
final (accentColor, icon, asciiIcon) = _getStyleForType(notification.type);
return Material(
elevation: 8,
borderRadius: BorderRadius.circular(12),
color: bgColor,
child: InkWell(
onTap: onDismiss,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
// 아이콘
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.2),
shape: BoxShape.circle,
),
child: Icon(icon, color: iconColor, size: 24),
),
const SizedBox(width: 12),
// 텍스트
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
notification.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
return GestureDetector(
onTap: onDismiss,
child: Container(
decoration: BoxDecoration(
color: RetroColors.panelBg,
border: Border(
top: BorderSide(color: accentColor, width: 3),
left: BorderSide(color: accentColor, width: 3),
bottom: const BorderSide(color: RetroColors.panelBorderOuter, width: 3),
right: const BorderSide(color: RetroColors.panelBorderOuter, width: 3),
),
boxShadow: [
BoxShadow(
color: accentColor.withValues(alpha: 0.4),
blurRadius: 12,
spreadRadius: 2,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 헤더 바
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: accentColor.withValues(alpha: 0.3),
child: Row(
children: [
// ASCII 아이콘
Text(
asciiIcon,
style: TextStyle(
fontFamily: 'JetBrainsMono',
fontSize: 12,
color: accentColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
// 타입 표시
Expanded(
child: Text(
_getTypeLabel(notification.type),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: accentColor,
letterSpacing: 1,
),
),
if (notification.subtitle != null) ...[
const SizedBox(height: 2),
Text(
notification.subtitle!,
style: TextStyle(
fontSize: 13,
color: Colors.white.withValues(alpha: 0.8),
),
),
// 닫기 버튼
GestureDetector(
onTap: onDismiss,
child: const Text(
'[X]',
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: RetroColors.textDisabled,
),
],
],
),
),
),
],
),
// 닫기 버튼
IconButton(
icon: const Icon(Icons.close, color: Colors.white70, size: 20),
onPressed: onDismiss,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
),
// 본문
Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
// 아이콘 박스
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: RetroColors.panelBgLight,
border: Border.all(color: accentColor, width: 2),
),
child: Icon(icon, color: accentColor, size: 20),
),
const SizedBox(width: 12),
// 텍스트
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
notification.title,
style: const TextStyle(
fontFamily: 'PressStart2P',
fontSize: 9,
color: RetroColors.textLight,
),
),
if (notification.subtitle != null) ...[
const SizedBox(height: 4),
Text(
notification.subtitle!,
style: const TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: RetroColors.textDisabled,
),
),
],
],
),
),
],
),
],
),
),
],
),
),
);
}
(Color, IconData, Color) _getStyleForType(NotificationType type) {
/// 알림 타입별 레트로 스타일 (강조 색상, 아이콘, ASCII 아이콘)
(Color, IconData, String) _getStyleForType(NotificationType type) {
return switch (type) {
NotificationType.levelUp => (
const Color(0xFF1565C0),
Icons.trending_up,
Colors.amber,
RetroColors.gold,
Icons.arrow_upward,
'',
),
NotificationType.questComplete => (
const Color(0xFF2E7D32),
Icons.check_circle,
Colors.lightGreen,
RetroColors.expGreen,
Icons.check,
'',
),
NotificationType.actComplete => (
const Color(0xFF6A1B9A),
RetroColors.mpBlue,
Icons.flag,
Colors.purpleAccent,
'',
),
NotificationType.newSpell => (
const Color(0xFF4527A0),
const Color(0xFF9966FF),
Icons.auto_fix_high,
Colors.deepPurpleAccent,
'',
),
NotificationType.newEquipment => (
const Color(0xFFE65100),
const Color(0xFFFF9933),
Icons.shield,
Colors.orange,
'',
),
NotificationType.bossDefeat => (
const Color(0xFFC62828),
RetroColors.hpRed,
Icons.whatshot,
Colors.redAccent,
'',
),
NotificationType.gameSaved => (
const Color(0xFF00695C),
RetroColors.expGreen,
Icons.save,
Colors.tealAccent,
'💾',
),
NotificationType.info => (
const Color(0xFF0277BD),
RetroColors.mpBlue,
Icons.info_outline,
Colors.lightBlueAccent,
'',
),
NotificationType.warning => (
const Color(0xFFF57C00),
Icons.warning_amber,
Colors.amber,
const Color(0xFFFFCC00),
Icons.warning,
'',
),
};
}
/// 알림 타입 라벨
String _getTypeLabel(NotificationType type) {
return switch (type) {
NotificationType.levelUp => 'LEVEL UP',
NotificationType.questComplete => 'QUEST DONE',
NotificationType.actComplete => 'ACT CLEAR',
NotificationType.newSpell => 'NEW SPELL',
NotificationType.newEquipment => 'NEW ITEM',
NotificationType.bossDefeat => 'BOSS SLAIN',
NotificationType.gameSaved => 'SAVED',
NotificationType.info => 'INFO',
NotificationType.warning => 'WARNING',
};
}
}