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