Files
asciinevrdie/lib/src/shared/widgets/retro_dialog.dart
JiWoong Sul 94aad1f0fe feat(ui): 레트로 다이얼로그 및 앱 테마 개선
- RetroDialog 스타일 업데이트
- 앱 테마 색상 및 스타일 통일
- 버튼/텍스트 스타일 일관성 강화
2025-12-30 23:57:03 +09:00

329 lines
8.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:askiineverdie/src/shared/retro_colors.dart';
/// 레트로 스타일 다이얼로그 베이스 위젯
///
/// 8-bit RPG 스타일의 다이얼로그 프레임
/// 라이트/다크 모드 자동 지원
class RetroDialog extends StatelessWidget {
const RetroDialog({
super.key,
required this.title,
required this.child,
this.titleIcon,
this.maxWidth = 500,
this.maxHeight = 600,
this.accentColor,
});
final String title;
final Widget child;
final String? titleIcon;
final double maxWidth;
final double maxHeight;
final Color? accentColor;
@override
Widget build(BuildContext context) {
final accent = accentColor ?? RetroColors.goldOf(context);
final panelBackground = RetroColors.panelBgOf(context);
final borderColor = RetroColors.borderOf(context);
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
constraints: BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight),
decoration: BoxDecoration(
color: panelBackground,
border: Border(
top: BorderSide(color: accent, width: 3),
left: BorderSide(color: accent, width: 3),
bottom: BorderSide(color: borderColor, width: 3),
right: BorderSide(color: borderColor, width: 3),
),
boxShadow: [
BoxShadow(
color: accent.withValues(alpha: 0.3),
blurRadius: 20,
spreadRadius: 2,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 헤더 바
_buildHeader(context, accent),
// 본문
Flexible(child: child),
],
),
),
);
}
Widget _buildHeader(BuildContext context, Color accent) {
final mutedColor = RetroColors.textMutedOf(context);
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: accent.withValues(alpha: 0.2),
border: Border(
bottom: BorderSide(color: accent, width: 2),
),
),
child: Row(
children: [
if (titleIcon != null) ...[
Text(
titleIcon!,
style: TextStyle(fontSize: 14, color: accent),
),
const SizedBox(width: 8),
],
Expanded(
child: Text(
title.toUpperCase(),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 10,
color: accent,
letterSpacing: 1,
),
),
),
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
border: Border.all(color: mutedColor, width: 1),
),
child: Text(
'X',
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: mutedColor,
),
),
),
),
],
),
);
}
}
/// 레트로 스타일 탭 바
/// 라이트/다크 모드 자동 지원
class RetroTabBar extends StatelessWidget {
const RetroTabBar({
super.key,
required this.controller,
required this.tabs,
this.accentColor,
});
final TabController controller;
final List<String> tabs;
final Color? accentColor;
@override
Widget build(BuildContext context) {
final accent = accentColor ?? RetroColors.goldOf(context);
final borderColor = RetroColors.borderOf(context);
final mutedColor = RetroColors.textMutedOf(context);
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: borderColor, width: 2),
),
),
child: TabBar(
controller: controller,
isScrollable: tabs.length > 3,
indicator: BoxDecoration(
color: accent.withValues(alpha: 0.3),
border: Border(
bottom: BorderSide(color: accent, width: 2),
),
),
labelColor: accent,
unselectedLabelColor: mutedColor,
labelStyle: const TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
),
unselectedLabelStyle: const TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
),
dividerColor: Colors.transparent,
tabs: tabs.map((t) => Tab(text: t.toUpperCase())).toList(),
),
);
}
}
/// 레트로 스타일 섹션 헤더
/// 라이트/다크 모드 자동 지원
class RetroSectionHeader extends StatelessWidget {
const RetroSectionHeader({
super.key,
required this.title,
this.icon,
this.accentColor,
});
final String title;
final String? icon;
final Color? accentColor;
@override
Widget build(BuildContext context) {
final accent = accentColor ?? RetroColors.goldOf(context);
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
if (icon != null) ...[
Text(
icon!,
style: TextStyle(fontSize: 12, color: accent),
),
const SizedBox(width: 6),
],
Text(
title.toUpperCase(),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: accent,
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
height: 2,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
accent,
accent.withValues(alpha: 0.3),
Colors.transparent,
],
),
),
),
),
],
),
);
}
}
/// 레트로 스타일 정보 박스
/// 라이트/다크 모드 자동 지원
class RetroInfoBox extends StatelessWidget {
const RetroInfoBox({
super.key,
required this.content,
this.backgroundColor,
});
final String content;
final Color? backgroundColor;
@override
Widget build(BuildContext context) {
final bgColor = backgroundColor ?? RetroColors.backgroundOf(context);
final borderColor = RetroColors.borderOf(context);
final textColor = RetroColors.textPrimaryOf(context);
return Container(
width: double.infinity,
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: bgColor,
border: Border.all(color: borderColor, width: 1),
),
child: Text(
content,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: textColor,
height: 1.8,
),
),
);
}
}
/// 레트로 스타일 통계 행
/// 라이트/다크 모드 자동 지원
class RetroStatRow extends StatelessWidget {
const RetroStatRow({
super.key,
required this.label,
required this.value,
this.highlight = false,
this.highlightColor,
});
final String label;
final String value;
final bool highlight;
final Color? highlightColor;
@override
Widget build(BuildContext context) {
final accent = highlightColor ?? RetroColors.goldOf(context);
final mutedColor = RetroColors.textMutedOf(context);
final textColor = RetroColors.textPrimaryOf(context);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 6,
color: mutedColor,
),
),
Container(
padding: highlight
? const EdgeInsets.symmetric(horizontal: 6, vertical: 2)
: null,
decoration: highlight
? BoxDecoration(
color: accent.withValues(alpha: 0.2),
border: Border.all(color: accent, width: 1),
)
: null,
child: Text(
value,
style: TextStyle(
fontFamily: 'JetBrainsMono',
fontSize: 9,
color: highlight ? accent : textColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
}