From 94aad1f0fe90a3b9bfc036c414c3cb8275188bfd Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Tue, 30 Dec 2025 23:57:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=EB=A0=88=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EC=95=B1=20=ED=85=8C=EB=A7=88=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RetroDialog 스타일 업데이트 - 앱 테마 색상 및 스타일 통일 - 버튼/텍스트 스타일 일관성 강화 --- lib/src/app.dart | 227 ++++++++++++++++++----- lib/src/shared/widgets/retro_dialog.dart | 109 ++++++----- 2 files changed, 244 insertions(+), 92 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index b8d66dc..5a86724 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -96,71 +96,64 @@ class _AskiiNeverDieAppState extends State { super.dispose(); } - /// 라이트 테마 + /// 라이트 테마 (Classic Parchment 스타일) ThemeData get _lightTheme => ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF234361)), - scaffoldBackgroundColor: const Color(0xFFF4F5F7), - useMaterial3: true, - ); - - /// 다크 테마 (레트로 RPG 스타일) - ThemeData get _darkTheme => ThemeData( - colorScheme: RetroColors.colorScheme, - scaffoldBackgroundColor: RetroColors.deepBrown, + colorScheme: RetroColors.lightColorScheme, + scaffoldBackgroundColor: const Color(0xFFFAF4ED), useMaterial3: true, // 카드/다이얼로그 레트로 배경 - cardColor: RetroColors.panelBg, - dialogTheme: DialogThemeData( - backgroundColor: RetroColors.panelBg, - titleTextStyle: const TextStyle( + cardColor: const Color(0xFFF2E8DC), + dialogTheme: const DialogThemeData( + backgroundColor: Color(0xFFF2E8DC), + titleTextStyle: TextStyle( fontFamily: 'PressStart2P', fontSize: 12, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), ), // 앱바 레트로 스타일 - appBarTheme: AppBarTheme( - backgroundColor: RetroColors.darkBrown, - foregroundColor: RetroColors.textLight, - titleTextStyle: const TextStyle( + appBarTheme: const AppBarTheme( + backgroundColor: Color(0xFFF2E8DC), + foregroundColor: Color(0xFF1F1F28), + titleTextStyle: TextStyle( fontFamily: 'PressStart2P', fontSize: 12, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), ), - // 버튼 테마 (inherit: false로 애니메이션 lerp 오류 방지) + // 버튼 테마 filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( - backgroundColor: RetroColors.buttonPrimary, - foregroundColor: RetroColors.textLight, + backgroundColor: const Color(0xFFE8DDD0), + foregroundColor: const Color(0xFF1F1F28), textStyle: const TextStyle( inherit: false, fontFamily: 'PressStart2P', fontSize: 10, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( - foregroundColor: RetroColors.gold, - side: const BorderSide(color: RetroColors.gold, width: 2), + foregroundColor: const Color(0xFFB8860B), + side: const BorderSide(color: Color(0xFFB8860B), width: 2), textStyle: const TextStyle( inherit: false, fontFamily: 'PressStart2P', fontSize: 10, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( - foregroundColor: RetroColors.cream, + foregroundColor: const Color(0xFF4A4458), textStyle: const TextStyle( inherit: false, fontFamily: 'PressStart2P', fontSize: 10, - color: RetroColors.cream, + color: Color(0xFF4A4458), ), ), ), @@ -169,71 +162,205 @@ class _AskiiNeverDieAppState extends State { headlineLarge: TextStyle( fontFamily: 'PressStart2P', fontSize: 18, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), headlineMedium: TextStyle( fontFamily: 'PressStart2P', fontSize: 14, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), headlineSmall: TextStyle( fontFamily: 'PressStart2P', fontSize: 12, - color: RetroColors.gold, + color: Color(0xFFB8860B), ), titleLarge: TextStyle( fontFamily: 'PressStart2P', fontSize: 12, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), titleMedium: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), titleSmall: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), - bodyLarge: TextStyle(fontSize: 14, color: RetroColors.textLight), - bodyMedium: TextStyle(fontSize: 12, color: RetroColors.textLight), - bodySmall: TextStyle(fontSize: 10, color: RetroColors.textLight), + bodyLarge: TextStyle(fontSize: 14, color: Color(0xFF1F1F28)), + bodyMedium: TextStyle(fontSize: 12, color: Color(0xFF1F1F28)), + bodySmall: TextStyle(fontSize: 10, color: Color(0xFF1F1F28)), labelLarge: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), labelMedium: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), labelSmall: TextStyle( fontFamily: 'PressStart2P', fontSize: 6, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), ), // 칩 테마 - chipTheme: ChipThemeData( - backgroundColor: RetroColors.panelBgLight, - labelStyle: const TextStyle( + chipTheme: const ChipThemeData( + backgroundColor: Color(0xFFE8DDD0), + labelStyle: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, - color: RetroColors.textLight, + color: Color(0xFF1F1F28), ), - side: const BorderSide(color: RetroColors.panelBorderInner), + side: BorderSide(color: Color(0xFF8B7355)), ), // 리스트 타일 테마 listTileTheme: const ListTileThemeData( - textColor: RetroColors.textLight, - iconColor: RetroColors.gold, + textColor: Color(0xFF1F1F28), + iconColor: Color(0xFFB8860B), ), // 프로그레스 인디케이터 progressIndicatorTheme: const ProgressIndicatorThemeData( - color: RetroColors.gold, - linearTrackColor: RetroColors.panelBorderOuter, + color: Color(0xFFB8860B), + linearTrackColor: Color(0xFFD4C4B0), + ), + ); + + /// 다크 테마 (Dark Fantasy 스타일) + ThemeData get _darkTheme => ThemeData( + colorScheme: RetroColors.darkColorScheme, + scaffoldBackgroundColor: RetroColors.deepBrown, + useMaterial3: true, + // 카드/다이얼로그 레트로 배경 + cardColor: RetroColors.darkBrown, + dialogTheme: const DialogThemeData( + backgroundColor: Color(0xFF24283B), + titleTextStyle: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 12, + color: Color(0xFFE0AF68), + ), + ), + // 앱바 레트로 스타일 + appBarTheme: const AppBarTheme( + backgroundColor: Color(0xFF24283B), + foregroundColor: Color(0xFFC0CAF5), + titleTextStyle: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 12, + color: Color(0xFFE0AF68), + ), + ), + // 버튼 테마 (inherit: false로 애니메이션 lerp 오류 방지) + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + backgroundColor: const Color(0xFF3D4260), + foregroundColor: const Color(0xFFC0CAF5), + textStyle: const TextStyle( + inherit: false, + fontFamily: 'PressStart2P', + fontSize: 10, + color: Color(0xFFC0CAF5), + ), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: const Color(0xFFE0AF68), + side: const BorderSide(color: Color(0xFFE0AF68), width: 2), + textStyle: const TextStyle( + inherit: false, + fontFamily: 'PressStart2P', + fontSize: 10, + color: Color(0xFFE0AF68), + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: const Color(0xFFC0CAF5), + textStyle: const TextStyle( + inherit: false, + fontFamily: 'PressStart2P', + fontSize: 10, + color: Color(0xFFC0CAF5), + ), + ), + ), + // 텍스트 테마 + textTheme: const TextTheme( + headlineLarge: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 18, + color: Color(0xFFE0AF68), + ), + headlineMedium: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 14, + color: Color(0xFFE0AF68), + ), + headlineSmall: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 12, + color: Color(0xFFE0AF68), + ), + titleLarge: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 12, + color: Color(0xFFC0CAF5), + ), + titleMedium: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 10, + color: Color(0xFFC0CAF5), + ), + titleSmall: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 8, + color: Color(0xFFC0CAF5), + ), + bodyLarge: TextStyle(fontSize: 14, color: Color(0xFFC0CAF5)), + bodyMedium: TextStyle(fontSize: 12, color: Color(0xFFC0CAF5)), + bodySmall: TextStyle(fontSize: 10, color: Color(0xFFC0CAF5)), + labelLarge: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 10, + color: Color(0xFFC0CAF5), + ), + labelMedium: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 8, + color: Color(0xFFC0CAF5), + ), + labelSmall: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 6, + color: Color(0xFFC0CAF5), + ), + ), + // 칩 테마 + chipTheme: const ChipThemeData( + backgroundColor: Color(0xFF2A2E3F), + labelStyle: TextStyle( + fontFamily: 'PressStart2P', + fontSize: 8, + color: Color(0xFFC0CAF5), + ), + side: BorderSide(color: Color(0xFF545C7E)), + ), + // 리스트 타일 테마 + listTileTheme: const ListTileThemeData( + textColor: Color(0xFFC0CAF5), + iconColor: Color(0xFFE0AF68), + ), + // 프로그레스 인디케이터 + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: Color(0xFFE0AF68), + linearTrackColor: Color(0xFF3B4261), ), ); diff --git a/lib/src/shared/widgets/retro_dialog.dart b/lib/src/shared/widgets/retro_dialog.dart index 45c7b00..cb95a5b 100644 --- a/lib/src/shared/widgets/retro_dialog.dart +++ b/lib/src/shared/widgets/retro_dialog.dart @@ -5,6 +5,7 @@ import 'package:askiineverdie/src/shared/retro_colors.dart'; /// 레트로 스타일 다이얼로그 베이스 위젯 /// /// 8-bit RPG 스타일의 다이얼로그 프레임 +/// 라이트/다크 모드 자동 지원 class RetroDialog extends StatelessWidget { const RetroDialog({ super.key, @@ -13,7 +14,7 @@ class RetroDialog extends StatelessWidget { this.titleIcon, this.maxWidth = 500, this.maxHeight = 600, - this.accentColor = RetroColors.gold, + this.accentColor, }); final String title; @@ -21,25 +22,29 @@ class RetroDialog extends StatelessWidget { final String? titleIcon; final double maxWidth; final double maxHeight; - final Color accentColor; + 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: RetroColors.panelBg, + color: panelBackground, 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), + 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: accentColor.withValues(alpha: 0.3), + color: accent.withValues(alpha: 0.3), blurRadius: 20, spreadRadius: 2, ), @@ -49,7 +54,7 @@ class RetroDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ // 헤더 바 - _buildHeader(context), + _buildHeader(context, accent), // 본문 Flexible(child: child), ], @@ -58,14 +63,16 @@ class RetroDialog extends StatelessWidget { ); } - Widget _buildHeader(BuildContext context) { + 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: accentColor.withValues(alpha: 0.2), + color: accent.withValues(alpha: 0.2), border: Border( - bottom: BorderSide(color: accentColor, width: 2), + bottom: BorderSide(color: accent, width: 2), ), ), child: Row( @@ -73,7 +80,7 @@ class RetroDialog extends StatelessWidget { if (titleIcon != null) ...[ Text( titleIcon!, - style: TextStyle(fontSize: 14, color: accentColor), + style: TextStyle(fontSize: 14, color: accent), ), const SizedBox(width: 8), ], @@ -83,7 +90,7 @@ class RetroDialog extends StatelessWidget { style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, - color: accentColor, + color: accent, letterSpacing: 1, ), ), @@ -93,14 +100,14 @@ class RetroDialog extends StatelessWidget { child: Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( - border: Border.all(color: RetroColors.textDisabled, width: 1), + border: Border.all(color: mutedColor, width: 1), ), - child: const Text( + child: Text( 'X', style: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, - color: RetroColors.textDisabled, + color: mutedColor, ), ), ), @@ -112,37 +119,42 @@ class RetroDialog extends StatelessWidget { } /// 레트로 스타일 탭 바 +/// 라이트/다크 모드 자동 지원 class RetroTabBar extends StatelessWidget { const RetroTabBar({ super.key, required this.controller, required this.tabs, - this.accentColor = RetroColors.gold, + this.accentColor, }); final TabController controller; final List tabs; - final Color accentColor; + 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: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: RetroColors.panelBorderOuter, width: 2), + bottom: BorderSide(color: borderColor, width: 2), ), ), child: TabBar( controller: controller, isScrollable: tabs.length > 3, indicator: BoxDecoration( - color: accentColor.withValues(alpha: 0.3), + color: accent.withValues(alpha: 0.3), border: Border( - bottom: BorderSide(color: accentColor, width: 2), + bottom: BorderSide(color: accent, width: 2), ), ), - labelColor: accentColor, - unselectedLabelColor: RetroColors.textDisabled, + labelColor: accent, + unselectedLabelColor: mutedColor, labelStyle: const TextStyle( fontFamily: 'PressStart2P', fontSize: 7, @@ -159,20 +171,23 @@ class RetroTabBar extends StatelessWidget { } /// 레트로 스타일 섹션 헤더 +/// 라이트/다크 모드 자동 지원 class RetroSectionHeader extends StatelessWidget { const RetroSectionHeader({ super.key, required this.title, this.icon, - this.accentColor = RetroColors.gold, + this.accentColor, }); final String title; final String? icon; - final Color accentColor; + final Color? accentColor; @override Widget build(BuildContext context) { + final accent = accentColor ?? RetroColors.goldOf(context); + return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( @@ -180,7 +195,7 @@ class RetroSectionHeader extends StatelessWidget { if (icon != null) ...[ Text( icon!, - style: TextStyle(fontSize: 12, color: accentColor), + style: TextStyle(fontSize: 12, color: accent), ), const SizedBox(width: 6), ], @@ -189,7 +204,7 @@ class RetroSectionHeader extends StatelessWidget { style: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, - color: accentColor, + color: accent, ), ), const SizedBox(width: 8), @@ -199,8 +214,8 @@ class RetroSectionHeader extends StatelessWidget { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - accentColor, - accentColor.withValues(alpha: 0.3), + accent, + accent.withValues(alpha: 0.3), Colors.transparent, ], ), @@ -214,6 +229,7 @@ class RetroSectionHeader extends StatelessWidget { } /// 레트로 스타일 정보 박스 +/// 라이트/다크 모드 자동 지원 class RetroInfoBox extends StatelessWidget { const RetroInfoBox({ super.key, @@ -226,19 +242,23 @@ class RetroInfoBox extends StatelessWidget { @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: backgroundColor ?? RetroColors.deepBrown, - border: Border.all(color: RetroColors.panelBorderOuter, width: 1), + color: bgColor, + border: Border.all(color: borderColor, width: 1), ), child: Text( content, - style: const TextStyle( + style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, - color: RetroColors.textLight, + color: textColor, height: 1.8, ), ), @@ -247,22 +267,27 @@ class RetroInfoBox extends StatelessWidget { } /// 레트로 스타일 통계 행 +/// 라이트/다크 모드 자동 지원 class RetroStatRow extends StatelessWidget { const RetroStatRow({ super.key, required this.label, required this.value, this.highlight = false, - this.highlightColor = RetroColors.gold, + this.highlightColor, }); final String label; final String value; final bool highlight; - final Color highlightColor; + 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( @@ -270,10 +295,10 @@ class RetroStatRow extends StatelessWidget { children: [ Text( label, - style: const TextStyle( + style: TextStyle( fontFamily: 'PressStart2P', fontSize: 6, - color: RetroColors.textDisabled, + color: mutedColor, ), ), Container( @@ -282,8 +307,8 @@ class RetroStatRow extends StatelessWidget { : null, decoration: highlight ? BoxDecoration( - color: highlightColor.withValues(alpha: 0.2), - border: Border.all(color: highlightColor, width: 1), + color: accent.withValues(alpha: 0.2), + border: Border.all(color: accent, width: 1), ) : null, child: Text( @@ -291,7 +316,7 @@ class RetroStatRow extends StatelessWidget { style: TextStyle( fontFamily: 'JetBrainsMono', fontSize: 9, - color: highlight ? highlightColor : RetroColors.textLight, + color: highlight ? accent : textColor, fontWeight: FontWeight.bold, ), ),