refactor(ui): 화면 및 위젯 정리

- GamePlayScreen build() 메서드 분할 (300→15 LOC)
- 애니메이션/프로그레스 패널 개선
- 설정 화면 정리
This commit is contained in:
JiWoong Sul
2026-01-21 17:34:47 +09:00
parent faf87eccb0
commit d9a2fe358c
7 changed files with 275 additions and 294 deletions

View File

@@ -1,5 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n;
/// 알림 타입 (Notification Type) /// 알림 타입 (Notification Type)
enum NotificationType { enum NotificationType {
levelUp, // 레벨업 levelUp, // 레벨업
@@ -62,8 +64,8 @@ class NotificationService {
show( show(
GameNotification( GameNotification(
type: NotificationType.levelUp, type: NotificationType.levelUp,
title: 'LEVEL UP!', title: game_l10n.notifyLevelUp,
subtitle: 'Level $newLevel', subtitle: game_l10n.notifyLevel(newLevel),
data: {'level': newLevel}, data: {'level': newLevel},
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
), ),
@@ -75,7 +77,7 @@ class NotificationService {
show( show(
GameNotification( GameNotification(
type: NotificationType.questComplete, type: NotificationType.questComplete,
title: 'QUEST COMPLETE!', title: game_l10n.notifyQuestComplete,
subtitle: questName, subtitle: questName,
data: {'quest': questName}, data: {'quest': questName},
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
@@ -87,8 +89,8 @@ class NotificationService {
/// actNumber: 0=프롤로그, 1=Act I, 2=Act II, ... /// actNumber: 0=프롤로그, 1=Act I, 2=Act II, ...
void showActComplete(int actNumber) { void showActComplete(int actNumber) {
final title = actNumber == 0 final title = actNumber == 0
? 'PROLOGUE COMPLETE!' ? game_l10n.notifyPrologueComplete
: 'ACT $actNumber COMPLETE!'; : game_l10n.notifyActComplete(actNumber);
show( show(
GameNotification( GameNotification(
type: NotificationType.actComplete, type: NotificationType.actComplete,
@@ -103,7 +105,7 @@ class NotificationService {
show( show(
GameNotification( GameNotification(
type: NotificationType.newSpell, type: NotificationType.newSpell,
title: 'NEW SPELL!', title: game_l10n.notifyNewSpell,
subtitle: spellName, subtitle: spellName,
data: {'spell': spellName}, data: {'spell': spellName},
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
@@ -116,7 +118,7 @@ class NotificationService {
show( show(
GameNotification( GameNotification(
type: NotificationType.newEquipment, type: NotificationType.newEquipment,
title: 'NEW EQUIPMENT!', title: game_l10n.notifyNewEquipment,
subtitle: equipmentName, subtitle: equipmentName,
data: {'equipment': equipmentName, 'slot': slot}, data: {'equipment': equipmentName, 'slot': slot},
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
@@ -129,7 +131,7 @@ class NotificationService {
show( show(
GameNotification( GameNotification(
type: NotificationType.bossDefeat, type: NotificationType.bossDefeat,
title: 'BOSS DEFEATED!', title: game_l10n.notifyBossDefeated,
subtitle: bossName, subtitle: bossName,
data: {'boss': bossName}, data: {'boss': bossName},
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),

View File

@@ -217,9 +217,9 @@ class _AnimationPanel extends StatelessWidget {
children: [ children: [
const Icon(Icons.auto_awesome, color: RetroColors.gold, size: 18), const Icon(Icons.auto_awesome, color: RetroColors.gold, size: 18),
const SizedBox(width: 10), const SizedBox(width: 10),
const Text( Text(
'ASCII NEVER DIE', L10n.of(context).appTitle.toUpperCase(),
style: TextStyle( style: const TextStyle(
fontFamily: 'PressStart2P', fontFamily: 'PressStart2P',
fontSize: 14, fontSize: 14,
color: RetroColors.gold, color: RetroColors.gold,
@@ -272,7 +272,7 @@ class _ActionButtons extends StatelessWidget {
final l10n = L10n.of(context); final l10n = L10n.of(context);
return RetroPanel( return RetroPanel(
title: 'MENU', title: L10n.of(context).menuTitle,
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@@ -551,11 +551,21 @@ class _GamePlayScreenState extends State<GamePlayScreen>
return const Scaffold(body: Center(child: CircularProgressIndicator())); return const Scaffold(body: Center(child: CircularProgressIndicator()));
} }
// 로케일 변경 시 전체 위젯 트리 강제 리빌드를 위한 Key
final localeKey = ValueKey(game_l10n.currentGameLocale); final localeKey = ValueKey(game_l10n.currentGameLocale);
// 캐로셀 레이아웃 사용 여부 확인
if (_shouldUseCarouselLayout(context)) { if (_shouldUseCarouselLayout(context)) {
return _buildMobileLayout(context, state, localeKey);
}
return _buildDesktopLayout(context, state, localeKey);
}
/// 모바일 캐로셀 레이아웃
Widget _buildMobileLayout(
BuildContext context,
GameState state,
ValueKey<String> localeKey,
) {
return NotificationOverlay( return NotificationOverlay(
key: localeKey, key: localeKey,
notificationService: _notificationService, notificationService: _notificationService,
@@ -585,7 +595,6 @@ class _GamePlayScreenState extends State<GamePlayScreen>
widget.controller.loop?.setSpeed(speed); widget.controller.loop?.setSpeed(speed);
setState(() {}); setState(() {});
}, },
// 특수 애니메이션 중에는 일시정지 상태로 표시하지 않음
isPaused: isPaused:
!widget.controller.isRunning && _specialAnimation == null, !widget.controller.isRunning && _specialAnimation == null,
onPauseToggle: () async { onPauseToggle: () async {
@@ -605,13 +614,9 @@ class _GamePlayScreenState extends State<GamePlayScreen>
notificationService: _notificationService, notificationService: _notificationService,
specialAnimation: _specialAnimation, specialAnimation: _specialAnimation,
onLanguageChange: (locale) async { onLanguageChange: (locale) async {
// navigator 참조를 async gap 전에 저장
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
// 1. 현재 상태 저장
await widget.controller.pause(saveOnStop: true); await widget.controller.pause(saveOnStop: true);
// 2. 로케일 변경
game_l10n.setGameLocale(locale); game_l10n.setGameLocale(locale);
// 3. 화면 재생성 (전체 UI 재구성)
if (context.mounted) { if (context.mounted) {
await widget.controller.resume(); await widget.controller.resume();
navigator.pushReplacement( navigator.pushReplacement(
@@ -625,16 +630,12 @@ class _GamePlayScreenState extends State<GamePlayScreen>
} }
}, },
onDeleteSaveAndNewGame: () async { onDeleteSaveAndNewGame: () async {
// 게임 루프 중지
await widget.controller.pause(saveOnStop: false); await widget.controller.pause(saveOnStop: false);
// 세이브 파일 삭제
await widget.controller.saveManager.deleteSave(); await widget.controller.saveManager.deleteSave();
// 캐릭터 생성 화면으로 돌아가기
if (context.mounted) { if (context.mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}, },
// 사운드 설정
bgmVolume: _audioController.bgmVolume, bgmVolume: _audioController.bgmVolume,
sfxVolume: _audioController.sfxVolume, sfxVolume: _audioController.sfxVolume,
onBgmVolumeChange: (volume) { onBgmVolumeChange: (volume) {
@@ -645,14 +646,11 @@ class _GamePlayScreenState extends State<GamePlayScreen>
_audioController.setSfxVolume(volume); _audioController.setSfxVolume(volume);
setState(() {}); setState(() {});
}, },
// 통계 및 도움말
onShowStatistics: () => _showStatisticsDialog(context), onShowStatistics: () => _showStatisticsDialog(context),
onShowHelp: () => HelpDialog.show(context), onShowHelp: () => HelpDialog.show(context),
// 치트 (디버그 모드)
cheatsEnabled: widget.controller.cheatsEnabled, cheatsEnabled: widget.controller.cheatsEnabled,
onCheatTask: () => widget.controller.loop?.cheatCompleteTask(), onCheatTask: () => widget.controller.loop?.cheatCompleteTask(),
onCheatQuest: () => onCheatQuest: () => widget.controller.loop?.cheatCompleteQuest(),
widget.controller.loop?.cheatCompleteQuest(),
onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(), onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(),
onCreateTestCharacter: () async { onCreateTestCharacter: () async {
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
@@ -661,7 +659,6 @@ class _GamePlayScreenState extends State<GamePlayScreen>
navigator.popUntil((route) => route.isFirst); navigator.popUntil((route) => route.isFirst);
} }
}, },
// 수익화 버프 (자동부활, 광고배속)
autoReviveEndMs: widget.controller.monetization.autoReviveEndMs, autoReviveEndMs: widget.controller.monetization.autoReviveEndMs,
speedBoostEndMs: widget.controller.monetization.speedBoostEndMs, speedBoostEndMs: widget.controller.monetization.speedBoostEndMs,
isPaidUser: widget.controller.monetization.isPaidUser, isPaidUser: widget.controller.monetization.isPaidUser,
@@ -670,31 +667,19 @@ class _GamePlayScreenState extends State<GamePlayScreen>
adSpeedMultiplier: widget.controller.adSpeedMultiplier, adSpeedMultiplier: widget.controller.adSpeedMultiplier,
has2xUnlocked: widget.controller.has2xUnlocked, has2xUnlocked: widget.controller.has2xUnlocked,
), ),
// 사망 오버레이 ..._buildOverlays(state),
if (state.isDead && state.deathInfo != null)
DeathOverlay(
deathInfo: state.deathInfo!,
traits: state.traits,
onResurrect: _handleResurrect,
onAdRevive: _handleAdRevive,
isPaidUser: IAPService.instance.isAdRemovalPurchased,
),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete)
VictoryOverlay(
traits: state.traits,
stats: state.stats,
progress: state.progress,
elapsedMs: state.skillSystem.elapsedMs,
onComplete: _handleVictoryComplete,
),
], ],
), ),
), ),
); );
} }
// 기존 데스크톱 레이아웃 (레트로 스타일) /// 데스크톱 3패널 레이아웃
Widget _buildDesktopLayout(
BuildContext context,
GameState state,
ValueKey<String> localeKey,
) {
return NotificationOverlay( return NotificationOverlay(
key: localeKey, key: localeKey,
notificationService: _notificationService, notificationService: _notificationService,
@@ -710,13 +695,27 @@ class _GamePlayScreenState extends State<GamePlayScreen>
} }
} }
}, },
// 웹/데스크톱 키보드 단축키 지원
child: Focus( child: Focus(
autofocus: true, autofocus: true,
onKeyEvent: (node, event) => _handleKeyboardShortcut(event, context), onKeyEvent: (node, event) => _handleKeyboardShortcut(event, context),
child: Scaffold( child: Scaffold(
backgroundColor: RetroColors.deepBrown, backgroundColor: RetroColors.deepBrown,
appBar: AppBar( appBar: _buildDesktopAppBar(context, state),
body: Stack(
children: [
_buildDesktopMainContent(state),
..._buildOverlays(state),
],
),
),
),
),
);
}
/// 데스크톱 앱바
PreferredSizeWidget _buildDesktopAppBar(BuildContext context, GameState state) {
return AppBar(
backgroundColor: RetroColors.darkBrown, backgroundColor: RetroColors.darkBrown,
title: Text( title: Text(
L10n.of(context).progressQuestTitle(state.traits.name), L10n.of(context).progressQuestTitle(state.traits.name),
@@ -727,65 +726,54 @@ class _GamePlayScreenState extends State<GamePlayScreen>
), ),
), ),
actions: [ actions: [
// 치트 버튼 (디버그용)
if (widget.controller.cheatsEnabled) ...[ if (widget.controller.cheatsEnabled) ...[
IconButton( IconButton(
icon: const Text('L+1'), icon: const Text('L+1'),
tooltip: L10n.of(context).levelUp, tooltip: L10n.of(context).levelUp,
onPressed: () => onPressed: () => widget.controller.loop?.cheatCompleteTask(),
widget.controller.loop?.cheatCompleteTask(),
), ),
IconButton( IconButton(
icon: const Text('Q!'), icon: const Text('Q!'),
tooltip: L10n.of(context).completeQuest, tooltip: L10n.of(context).completeQuest,
onPressed: () => onPressed: () => widget.controller.loop?.cheatCompleteQuest(),
widget.controller.loop?.cheatCompleteQuest(),
), ),
IconButton( IconButton(
icon: const Text('P!'), icon: const Text('P!'),
tooltip: L10n.of(context).completePlot, tooltip: L10n.of(context).completePlot,
onPressed: () => onPressed: () => widget.controller.loop?.cheatCompletePlot(),
widget.controller.loop?.cheatCompletePlot(),
), ),
], ],
// 통계 버튼
IconButton( IconButton(
icon: const Icon(Icons.bar_chart), icon: const Icon(Icons.bar_chart),
tooltip: game_l10n.uiStatistics, tooltip: game_l10n.uiStatistics,
onPressed: () => _showStatisticsDialog(context), onPressed: () => _showStatisticsDialog(context),
), ),
// 도움말 버튼
IconButton( IconButton(
icon: const Icon(Icons.help_outline), icon: const Icon(Icons.help_outline),
tooltip: game_l10n.uiHelp, tooltip: game_l10n.uiHelp,
onPressed: () => HelpDialog.show(context), onPressed: () => HelpDialog.show(context),
), ),
// 설정 버튼
IconButton( IconButton(
icon: const Icon(Icons.settings), icon: const Icon(Icons.settings),
tooltip: game_l10n.uiSettings, tooltip: game_l10n.uiSettings,
onPressed: () => _showSettingsScreen(context), onPressed: () => _showSettingsScreen(context),
), ),
], ],
), );
body: Stack( }
/// 데스크톱 메인 컨텐츠 (3패널)
Widget _buildDesktopMainContent(GameState state) {
return Column(
children: [ children: [
// 메인 게임 UI
Column(
children: [
// 상단: ASCII 애니메이션 + Task Progress (Phase 7: 고정 4색 팔레트)
TaskProgressPanel( TaskProgressPanel(
progress: state.progress, progress: state.progress,
speedMultiplier: speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1,
widget.controller.loop?.speedMultiplier ?? 1,
onSpeedCycle: () { onSpeedCycle: () {
widget.controller.loop?.cycleSpeed(); widget.controller.loop?.cycleSpeed();
setState(() {}); setState(() {});
}, },
// 특수 애니메이션 중에는 일시정지 상태로 표시하지 않음 isPaused: !widget.controller.isRunning && _specialAnimation == null,
isPaused:
!widget.controller.isRunning &&
_specialAnimation == null,
onPauseToggle: () async { onPauseToggle: () async {
await widget.controller.togglePause(); await widget.controller.togglePause();
setState(() {}); setState(() {});
@@ -801,27 +789,23 @@ class _GamePlayScreenState extends State<GamePlayScreen>
state.progress.currentCombat?.recentEvents.lastOrNull, state.progress.currentCombat?.recentEvents.lastOrNull,
raceId: state.traits.raceId, raceId: state.traits.raceId,
), ),
// 메인 3패널 영역
Expanded( Expanded(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// 좌측 패널: Character Sheet
Expanded(flex: 2, child: _buildCharacterPanel(state)), Expanded(flex: 2, child: _buildCharacterPanel(state)),
// 중앙 패널: Equipment/Inventory
Expanded(flex: 3, child: _buildEquipmentPanel(state)), Expanded(flex: 3, child: _buildEquipmentPanel(state)),
// 우측 패널: Plot/Quest
Expanded(flex: 2, child: _buildQuestPanel(state)), Expanded(flex: 2, child: _buildQuestPanel(state)),
], ],
), ),
), ),
], ],
), );
}
// 사망 오버레이 /// 공통 오버레이 (사망, 승리)
List<Widget> _buildOverlays(GameState state) {
return [
if (state.isDead && state.deathInfo != null) if (state.isDead && state.deathInfo != null)
DeathOverlay( DeathOverlay(
deathInfo: state.deathInfo!, deathInfo: state.deathInfo!,
@@ -830,7 +814,6 @@ class _GamePlayScreenState extends State<GamePlayScreen>
onAdRevive: _handleAdRevive, onAdRevive: _handleAdRevive,
isPaidUser: IAPService.instance.isAdRemovalPurchased, isPaidUser: IAPService.instance.isAdRemovalPurchased,
), ),
// 승리 오버레이 (게임 클리어)
if (widget.controller.isComplete) if (widget.controller.isComplete)
VictoryOverlay( VictoryOverlay(
traits: state.traits, traits: state.traits,
@@ -839,12 +822,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
elapsedMs: state.skillSystem.elapsedMs, elapsedMs: state.skillSystem.elapsedMs,
onComplete: _handleVictoryComplete, onComplete: _handleVictoryComplete,
), ),
], ];
),
),
),
),
);
} }
/// 키보드 단축키 핸들러 (웹/데스크톱) /// 키보드 단축키 핸들러 (웹/데스크톱)

View File

@@ -785,7 +785,9 @@ class _AsciiAnimationCardState extends State<AsciiAnimationCard> {
child: AsciiDisintegrateWidget( child: AsciiDisintegrateWidget(
characterLines: _deathAnimationMonsterLines!, characterLines: _deathAnimationMonsterLines!,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
textColor: widget.monsterGrade?.displayColor, textColor: widget.monsterGrade?.displayColorCode != null
? Color(widget.monsterGrade!.displayColorCode!)
: null,
onComplete: _onDeathAnimationComplete, onComplete: _onDeathAnimationComplete,
), ),
), ),

View File

@@ -756,9 +756,10 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
final gradePrefix = (isKillTask && grade != null) final gradePrefix = (isKillTask && grade != null)
? grade.displayPrefix ? grade.displayPrefix
: ''; : '';
final gradeColor = (isKillTask && grade != null) final gradeColorCode = (isKillTask && grade != null)
? grade.displayColor ? grade.displayColorCode
: null; : null;
final gradeColor = gradeColorCode != null ? Color(gradeColorCode) : null;
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

View File

@@ -169,9 +169,10 @@ class TaskProgressPanel extends StatelessWidget {
final gradePrefix = (isKillTask && grade != null) final gradePrefix = (isKillTask && grade != null)
? grade.displayPrefix ? grade.displayPrefix
: ''; : '';
final gradeColor = (isKillTask && grade != null) final gradeColorCode = (isKillTask && grade != null)
? grade.displayColor ? grade.displayColorCode
: null; : null;
final gradeColor = gradeColorCode != null ? Color(gradeColorCode) : null;
return Text.rich( return Text.rich(
TextSpan( TextSpan(

View File

@@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n; import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n;
import 'package:asciineverdie/l10n/app_localizations.dart';
import 'package:asciineverdie/src/core/engine/debug_settings_service.dart'; import 'package:asciineverdie/src/core/engine/debug_settings_service.dart';
import 'package:asciineverdie/src/core/storage/settings_repository.dart'; import 'package:asciineverdie/src/core/storage/settings_repository.dart';
import 'package:asciineverdie/src/shared/retro_colors.dart'; import 'package:asciineverdie/src/shared/retro_colors.dart';
@@ -145,7 +146,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
// 디버그 섹션 (디버그 모드에서만 표시) // 디버그 섹션 (디버그 모드에서만 표시)
if (kDebugMode) ...[ if (kDebugMode) ...[
const SizedBox(height: 16), const SizedBox(height: 16),
const _RetroSectionTitle(title: 'DEBUG'), _RetroSectionTitle(title: L10n.of(context).debugTitle),
const SizedBox(height: 8), const SizedBox(height: 8),
_buildDebugSection(context), _buildDebugSection(context),
], ],
@@ -308,7 +309,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'DEVELOPER TOOLS', L10n.of(context).debugDeveloperTools,
style: TextStyle( style: TextStyle(
fontFamily: 'PressStart2P', fontFamily: 'PressStart2P',
fontSize: 10, fontSize: 10,
@@ -322,8 +323,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
// IAP 시뮬레이션 토글 // IAP 시뮬레이션 토글
_RetroDebugToggle( _RetroDebugToggle(
icon: Icons.shopping_cart, icon: Icons.shopping_cart,
label: 'IAP PURCHASED', label: L10n.of(context).debugIapPurchased,
description: 'ON: 유료 유저로 동작 (광고 제거)', description: L10n.of(context).debugIapPurchasedDesc,
value: _debugIapSimulated, value: _debugIapSimulated,
onChanged: (value) async { onChanged: (value) async {
await DebugSettingsService.instance.setIapSimulated(value); await DebugSettingsService.instance.setIapSimulated(value);
@@ -346,7 +347,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
// 테스트 캐릭터 생성 // 테스트 캐릭터 생성
if (widget.onCreateTestCharacter != null) ...[ if (widget.onCreateTestCharacter != null) ...[
Text( Text(
'현재 캐릭터를 레벨 100으로 수정하여\n명예의 전당에 등록합니다.', L10n.of(context).debugTestCharacterDesc,
style: TextStyle( style: TextStyle(
fontFamily: 'PressStart2P', fontFamily: 'PressStart2P',
fontSize: 7, fontSize: 7,
@@ -358,7 +359,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: RetroTextButton( child: RetroTextButton(
text: 'CREATE TEST CHARACTER', text: L10n.of(context).debugCreateTestCharacter,
icon: Icons.science, icon: Icons.science,
onPressed: _handleCreateTestCharacter, onPressed: _handleCreateTestCharacter,
), ),
@@ -388,7 +389,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'OFFLINE HOURS', L10n.of(context).debugOfflineHours,
style: TextStyle( style: TextStyle(
fontFamily: 'PressStart2P', fontFamily: 'PressStart2P',
fontSize: 8, fontSize: 8,
@@ -397,7 +398,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
'복귀 보상 테스트 (재시작 시 적용)', L10n.of(context).debugOfflineHoursDesc,
style: TextStyle( style: TextStyle(
fontFamily: 'PressStart2P', fontFamily: 'PressStart2P',
fontSize: 6, fontSize: 6,
@@ -435,14 +436,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (context) => _RetroConfirmDialog( builder: (context) => _RetroConfirmDialog(
title: 'CREATE TEST CHARACTER?', title: L10n.of(context).debugCreateTestCharacterTitle,
message: message: L10n.of(context).debugCreateTestCharacterMessage,
'현재 캐릭터가 레벨 100으로 변환되어\n' confirmText: L10n.of(context).createButton,
'명예의 전당에 등록됩니다.\n\n' cancelText: L10n.of(context).cancel.toUpperCase(),
'⚠️ 현재 세이브 파일이 삭제됩니다.\n'
'이 작업은 되돌릴 수 없습니다.',
confirmText: 'CREATE',
cancelText: 'CANCEL',
), ),
); );