refactor(ui): 기타 화면 정리

- FrontScreen, HallOfFameScreen 개선
- NewCharacterScreen, SettingsScreen 정리
- App 초기화 로직 정리
This commit is contained in:
JiWoong Sul
2026-01-12 16:17:25 +09:00
parent cbbbbba1a5
commit 448f500ca0
7 changed files with 287 additions and 131 deletions

View File

@@ -469,7 +469,8 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
} }
void _navigateToNewCharacter(BuildContext context) { void _navigateToNewCharacter(BuildContext context) {
Navigator.of(context).push( Navigator.of(context)
.push(
MaterialPageRoute<void>( MaterialPageRoute<void>(
builder: (context) => NewCharacterScreen( builder: (context) => NewCharacterScreen(
onCharacterCreated: (initialState, {bool testMode = false}) { onCharacterCreated: (initialState, {bool testMode = false}) {
@@ -477,7 +478,8 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
}, },
), ),
), ),
).then((_) { )
.then((_) {
// 새 게임 후 돌아오면 세이브 정보 갱신 (BGM은 _checkForExistingSave에서 재생) // 새 게임 후 돌아오면 세이브 정보 갱신 (BGM은 _checkForExistingSave에서 재생)
_checkForExistingSave(); _checkForExistingSave();
}); });
@@ -544,7 +546,8 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
} }
void _navigateToGame(BuildContext context) { void _navigateToGame(BuildContext context) {
Navigator.of(context).push( Navigator.of(context)
.push(
MaterialPageRoute<void>( MaterialPageRoute<void>(
builder: (context) => GamePlayScreen( builder: (context) => GamePlayScreen(
controller: _controller, controller: _controller,
@@ -555,7 +558,8 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
onThemeModeChange: _changeThemeMode, onThemeModeChange: _changeThemeMode,
), ),
), ),
).then((_) { )
.then((_) {
// 게임에서 돌아오면 세이브 정보 갱신 (BGM은 _checkForExistingSave에서 재생) // 게임에서 돌아오면 세이브 정보 갱신 (BGM은 _checkForExistingSave에서 재생)
_checkForExistingSave(); _checkForExistingSave();
}); });
@@ -563,9 +567,13 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
/// Phase 10: 명예의 전당 화면으로 이동 /// Phase 10: 명예의 전당 화면으로 이동
void _navigateToHallOfFame(BuildContext context) { void _navigateToHallOfFame(BuildContext context) {
Navigator.of(context).push( Navigator.of(context)
MaterialPageRoute<void>(builder: (context) => const HallOfFameScreen()), .push(
).then((_) { MaterialPageRoute<void>(
builder: (context) => const HallOfFameScreen(),
),
)
.then((_) {
// 명예의 전당에서 돌아오면 타이틀 BGM 재생 // 명예의 전당에서 돌아오면 타이틀 BGM 재생
_audioService.playBgm('title'); _audioService.playBgm('title');
}); });
@@ -573,11 +581,11 @@ class _AskiiNeverDieAppState extends State<AskiiNeverDieApp> {
/// 로컬 아레나 화면으로 이동 /// 로컬 아레나 화면으로 이동
void _navigateToArena(BuildContext context) { void _navigateToArena(BuildContext context) {
Navigator.of(context).push( Navigator.of(context)
MaterialPageRoute<void>( .push(
builder: (context) => const ArenaScreen(), MaterialPageRoute<void>(builder: (context) => const ArenaScreen()),
), )
).then((_) { .then((_) {
// 아레나에서 돌아오면 명예의 전당 다시 로드 및 타이틀 BGM 재생 // 아레나에서 돌아오면 명예의 전당 다시 로드 및 타이틀 BGM 재생
_loadHallOfFame(); _loadHallOfFame();
_audioService.playBgm('title'); _audioService.playBgm('title');
@@ -636,10 +644,7 @@ class _SplashScreen extends StatelessWidget {
fontSize: 14, fontSize: 14,
color: RetroColors.cream, color: RetroColors.cream,
shadows: [ shadows: [
Shadow( Shadow(color: RetroColors.brown, offset: Offset(1, 1)),
color: RetroColors.brown,
offset: Offset(1, 1),
),
], ],
), ),
), ),
@@ -648,10 +653,7 @@ class _SplashScreen extends StatelessWidget {
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
// 레트로 로딩 바 // 레트로 로딩 바
SizedBox( SizedBox(width: 160, child: _RetroLoadingBar()),
width: 160,
child: _RetroLoadingBar(),
),
], ],
), ),
), ),

View File

@@ -107,8 +107,8 @@ class FrontScreen extends StatelessWidget {
onHallOfFame: onHallOfFame != null onHallOfFame: onHallOfFame != null
? () => onHallOfFame!(context) ? () => onHallOfFame!(context)
: null, : null,
onLocalArena: onLocalArena != null && onLocalArena:
hallOfFameCount >= 2 onLocalArena != null && hallOfFameCount >= 2
? () => onLocalArena!(context) ? () => onLocalArena!(context)
: null, : null,
savedGamePreview: savedGamePreview, savedGamePreview: savedGamePreview,
@@ -153,10 +153,7 @@ class _RetroHeader extends StatelessWidget {
fontSize: 14, fontSize: 14,
color: RetroColors.gold, color: RetroColors.gold,
shadows: [ shadows: [
Shadow( Shadow(color: RetroColors.goldDark, offset: Offset(2, 2)),
color: RetroColors.goldDark,
offset: Offset(2, 2),
),
], ],
), ),
), ),
@@ -169,7 +166,10 @@ class _RetroHeader extends StatelessWidget {
spacing: 8, spacing: 8,
runSpacing: 8, runSpacing: 8,
children: [ children: [
_RetroTag(icon: Icons.cloud_off_outlined, label: l10n.tagNoNetwork), _RetroTag(
icon: Icons.cloud_off_outlined,
label: l10n.tagNoNetwork,
),
_RetroTag(icon: Icons.timer_outlined, label: l10n.tagIdleRpg), _RetroTag(icon: Icons.timer_outlined, label: l10n.tagIdleRpg),
_RetroTag(icon: Icons.storage_rounded, label: l10n.tagLocalSaves), _RetroTag(icon: Icons.storage_rounded, label: l10n.tagLocalSaves),
], ],

View File

@@ -159,9 +159,7 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final frame = _applyGlitchEffect( final frame = _applyGlitchEffect(frontScreenAnimationFrames[_currentFrame]);
frontScreenAnimationFrames[_currentFrame],
);
// 현재 종족 이름 (UI 표시용) // 현재 종족 이름 (UI 표시용)
final raceName = RaceData.findById(_currentRaceId)?.name ?? 'Hero'; final raceName = RaceData.findById(_currentRaceId)?.name ?? 'Hero';
@@ -172,10 +170,7 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
// 항상 검은 배경 // 항상 검은 배경
color: Colors.black, color: Colors.black,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(color: Colors.white24, width: 1),
color: Colors.white24,
width: 1,
),
// 은은한 글로우 효과 // 은은한 글로우 효과
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@@ -192,9 +187,7 @@ class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
Flexible( Flexible(
child: FittedBox( child: FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: RichText( child: RichText(text: _buildColoredTextSpan(frame)),
text: _buildColoredTextSpan(frame),
),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:asciineverdie/data/game_text_l10n.dart' as l10n; import 'package:asciineverdie/data/game_text_l10n.dart' as l10n;
@@ -136,9 +137,7 @@ class _HallOfFameScreenState extends State<HallOfFameScreen> {
padding: const EdgeInsets.symmetric(vertical: 10), padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: goldColor.withValues(alpha: 0.2), color: goldColor.withValues(alpha: 0.2),
border: Border( border: Border(bottom: BorderSide(color: goldColor, width: 2)),
bottom: BorderSide(color: goldColor, width: 2),
),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -166,7 +165,11 @@ class _HallOfFameScreenState extends State<HallOfFameScreen> {
itemCount: hallOfFame.entries.length, itemCount: hallOfFame.entries.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final entry = hallOfFame.entries[index]; final entry = hallOfFame.entries[index];
return _HallOfFameEntryCard(entry: entry, rank: index + 1); return _HallOfFameEntryCard(
entry: entry,
rank: index + 1,
onDeleteRequest: () => _confirmDelete(entry),
);
}, },
), ),
), ),
@@ -174,14 +177,103 @@ class _HallOfFameScreenState extends State<HallOfFameScreen> {
), ),
); );
} }
/// 삭제 확인 다이얼로그 (디버그 모드 전용)
Future<void> _confirmDelete(HallOfFameEntry entry) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => RetroDialog(
title: l10n.uiConfirm.toUpperCase(),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${entry.characterName} (Lv.${entry.level})\n'
'${l10n.uiConfirmDelete}',
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 7),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context, false),
child: Text(l10n.uiCancel),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: RetroColors.hpOf(context),
),
onPressed: () => Navigator.pop(context, true),
child: Text(l10n.uiDelete),
),
],
),
],
),
),
),
);
if (confirmed == true && mounted) {
await _deleteEntry(entry.id);
}
}
/// 엔트리 삭제 실행
Future<void> _deleteEntry(String id) async {
final success = await _storage.deleteEntry(id);
if (!mounted) return;
if (success) {
// 성공 시 목록 새로고침
await _loadHallOfFame();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
l10n.uiDeleted,
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 6),
),
backgroundColor: RetroColors.mpOf(context),
duration: const Duration(seconds: 2),
),
);
}
} else {
// 실패 시 에러 메시지
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
l10n.uiError,
style: const TextStyle(fontFamily: 'PressStart2P', fontSize: 6),
),
backgroundColor: RetroColors.hpOf(context),
duration: const Duration(seconds: 2),
),
);
}
}
}
} }
/// 명예의 전당 엔트리 카드 /// 명예의 전당 엔트리 카드
class _HallOfFameEntryCard extends StatelessWidget { class _HallOfFameEntryCard extends StatelessWidget {
const _HallOfFameEntryCard({required this.entry, required this.rank}); const _HallOfFameEntryCard({
required this.entry,
required this.rank,
required this.onDeleteRequest,
});
final HallOfFameEntry entry; final HallOfFameEntry entry;
final int rank; final int rank;
final VoidCallback onDeleteRequest;
void _showDetailDialog(BuildContext context) { void _showDetailDialog(BuildContext context) {
showDialog<void>( showDialog<void>(
@@ -259,8 +351,9 @@ class _HallOfFameEntryCard extends StatelessWidget {
vertical: 2, vertical: 2,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: RetroColors.mpOf(context) color: RetroColors.mpOf(
.withValues(alpha: 0.2), context,
).withValues(alpha: 0.2),
border: Border.all( border: Border.all(
color: RetroColors.mpOf(context), color: RetroColors.mpOf(context),
width: 1, width: 1,
@@ -337,6 +430,36 @@ class _HallOfFameEntryCard extends StatelessWidget {
), ),
], ],
), ),
// 삭제 버튼 (디버그 모드 전용)
if (kDebugMode) ...[
const SizedBox(width: 8),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
// 이벤트 전파 중지 (카드 클릭 방지)
onDeleteRequest();
},
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: RetroColors.hpOf(
context,
).withValues(alpha: 0.2),
border: Border.all(
color: RetroColors.hpOf(context),
width: 1,
),
),
child: Icon(
Icons.delete_outline,
size: 16,
color: RetroColors.hpOf(context),
),
),
),
),
],
], ],
), ),
), ),
@@ -459,9 +582,7 @@ class _GameClearDialog extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: goldColor.withValues(alpha: 0.2), color: goldColor.withValues(alpha: 0.2),
border: Border( border: Border(bottom: BorderSide(color: goldColor, width: 2)),
bottom: BorderSide(color: goldColor, width: 2),
),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -496,10 +617,7 @@ class _GameClearDialog extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(height: 2, color: borderColor),
height: 2,
color: borderColor,
),
const SizedBox(height: 16), const SizedBox(height: 16),
// 캐릭터 정보 // 캐릭터 정보
Text( Text(
@@ -528,9 +646,21 @@ class _GameClearDialog extends StatelessWidget {
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
children: [ children: [
_buildStat(context, l10n.hofLevel, '${entry.level}'), _buildStat(context, l10n.hofLevel, '${entry.level}'),
_buildStat(context, l10n.hofTime, entry.formattedPlayTime), _buildStat(
_buildStat(context, l10n.hofDeaths, '${entry.totalDeaths}'), context,
_buildStat(context, l10n.hofQuests, '${entry.questsCompleted}'), l10n.hofTime,
entry.formattedPlayTime,
),
_buildStat(
context,
l10n.hofDeaths,
'${entry.totalDeaths}',
),
_buildStat(
context,
l10n.hofQuests,
'${entry.questsCompleted}',
),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -817,7 +947,12 @@ class _HallOfFameDetailDialog extends StatelessWidget {
spacing: 8, spacing: 8,
runSpacing: 6, runSpacing: 6,
children: [ children: [
_buildStatItem(context, Icons.timer, l10n.hofTime, entry.formattedPlayTime), _buildStatItem(
context,
Icons.timer,
l10n.hofTime,
entry.formattedPlayTime,
),
_buildStatItem( _buildStatItem(
context, context,
Icons.pest_control, Icons.pest_control,
@@ -860,7 +995,11 @@ class _HallOfFameDetailDialog extends StatelessWidget {
_buildCombatStatChip(l10n.statStr, '${stats.str}', Colors.red), _buildCombatStatChip(l10n.statStr, '${stats.str}', Colors.red),
_buildCombatStatChip(l10n.statCon, '${stats.con}', Colors.orange), _buildCombatStatChip(l10n.statCon, '${stats.con}', Colors.orange),
_buildCombatStatChip(l10n.statDex, '${stats.dex}', Colors.green), _buildCombatStatChip(l10n.statDex, '${stats.dex}', Colors.green),
_buildCombatStatChip(l10n.statInt, '${stats.intelligence}', Colors.blue), _buildCombatStatChip(
l10n.statInt,
'${stats.intelligence}',
Colors.blue,
),
_buildCombatStatChip(l10n.statWis, '${stats.wis}', Colors.purple), _buildCombatStatChip(l10n.statWis, '${stats.wis}', Colors.purple),
_buildCombatStatChip(l10n.statCha, '${stats.cha}', Colors.pink), _buildCombatStatChip(l10n.statCha, '${stats.cha}', Colors.pink),
], ],
@@ -873,8 +1012,16 @@ class _HallOfFameDetailDialog extends StatelessWidget {
spacing: 6, spacing: 6,
runSpacing: 4, runSpacing: 4,
children: [ children: [
_buildCombatStatChip(l10n.statAtk, '${stats.atk}', Colors.red.shade700), _buildCombatStatChip(
_buildCombatStatChip(l10n.statMAtk, '${stats.magAtk}', Colors.blue.shade700), l10n.statAtk,
'${stats.atk}',
Colors.red.shade700,
),
_buildCombatStatChip(
l10n.statMAtk,
'${stats.magAtk}',
Colors.blue.shade700,
),
_buildCombatStatChip( _buildCombatStatChip(
l10n.statCri, l10n.statCri,
'${(stats.criRate * 100).toStringAsFixed(1)}%', '${(stats.criRate * 100).toStringAsFixed(1)}%',
@@ -889,7 +1036,11 @@ class _HallOfFameDetailDialog extends StatelessWidget {
runSpacing: 4, runSpacing: 4,
children: [ children: [
_buildCombatStatChip(l10n.statDef, '${stats.def}', Colors.brown), _buildCombatStatChip(l10n.statDef, '${stats.def}', Colors.brown),
_buildCombatStatChip(l10n.statMDef, '${stats.magDef}', Colors.indigo), _buildCombatStatChip(
l10n.statMDef,
'${stats.magDef}',
Colors.indigo,
),
_buildCombatStatChip( _buildCombatStatChip(
l10n.statEva, l10n.statEva,
'${(stats.evasion * 100).toStringAsFixed(1)}%', '${(stats.evasion * 100).toStringAsFixed(1)}%',
@@ -908,8 +1059,16 @@ class _HallOfFameDetailDialog extends StatelessWidget {
spacing: 6, spacing: 6,
runSpacing: 4, runSpacing: 4,
children: [ children: [
_buildCombatStatChip(l10n.statHp, '${stats.hpMax}', Colors.red.shade400), _buildCombatStatChip(
_buildCombatStatChip(l10n.statMp, '${stats.mpMax}', Colors.blue.shade400), l10n.statHp,
'${stats.hpMax}',
Colors.red.shade400,
),
_buildCombatStatChip(
l10n.statMp,
'${stats.mpMax}',
Colors.blue.shade400,
),
], ],
), ),
], ],
@@ -1021,7 +1180,10 @@ class _HallOfFameDetailDialog extends StatelessWidget {
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: rarityColor.withValues(alpha: 0.1), color: rarityColor.withValues(alpha: 0.1),
border: Border.all(color: rarityColor.withValues(alpha: 0.3), width: 1), border: Border.all(
color: rarityColor.withValues(alpha: 0.3),
width: 1,
),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -1076,11 +1238,7 @@ class _HallOfFameDetailDialog extends StatelessWidget {
// 스탯 요약 // 스탯 요약
if (statSummary.isNotEmpty) ...[ if (statSummary.isNotEmpty) ...[
const SizedBox(height: 4), const SizedBox(height: 4),
Wrap( Wrap(spacing: 6, runSpacing: 2, children: statSummary),
spacing: 6,
runSpacing: 2,
children: statSummary,
),
], ],
], ],
), ),
@@ -1180,16 +1338,32 @@ class _HallOfFameDetailDialog extends StatelessWidget {
// 확률 스탯 // 확률 스탯
if (stats.criRate > 0) { if (stats.criRate > 0) {
addStat(l10n.statCri, '+${(stats.criRate * 100).toStringAsFixed(0)}%', Colors.amber); addStat(
l10n.statCri,
'+${(stats.criRate * 100).toStringAsFixed(0)}%',
Colors.amber,
);
} }
if (stats.blockRate > 0) { if (stats.blockRate > 0) {
addStat(l10n.statBlock, '+${(stats.blockRate * 100).toStringAsFixed(0)}%', Colors.blueGrey); addStat(
l10n.statBlock,
'+${(stats.blockRate * 100).toStringAsFixed(0)}%',
Colors.blueGrey,
);
} }
if (stats.evasion > 0) { if (stats.evasion > 0) {
addStat(l10n.statEva, '+${(stats.evasion * 100).toStringAsFixed(0)}%', Colors.teal); addStat(
l10n.statEva,
'+${(stats.evasion * 100).toStringAsFixed(0)}%',
Colors.teal,
);
} }
if (stats.parryRate > 0) { if (stats.parryRate > 0) {
addStat(l10n.statParry, '+${(stats.parryRate * 100).toStringAsFixed(0)}%', Colors.cyan); addStat(
l10n.statParry,
'+${(stats.parryRate * 100).toStringAsFixed(0)}%',
Colors.cyan,
);
} }
// 보너스 스탯 // 보너스 스탯
@@ -1227,10 +1401,7 @@ class _HallOfFameDetailDialog extends StatelessWidget {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: RetroColors.backgroundOf(context), color: RetroColors.backgroundOf(context),
border: Border.all( border: Border.all(color: RetroColors.borderOf(context), width: 1),
color: RetroColors.borderOf(context),
width: 1,
),
), ),
child: Text( child: Text(
l10n.hofNoSkills.toUpperCase(), l10n.hofNoSkills.toUpperCase(),
@@ -1255,7 +1426,10 @@ class _HallOfFameDetailDialog extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: skillColor.withValues(alpha: 0.1), color: skillColor.withValues(alpha: 0.1),
border: Border.all(color: skillColor.withValues(alpha: 0.3), width: 1), border: Border.all(
color: skillColor.withValues(alpha: 0.3),
width: 1,
),
), ),
child: Text( child: Text(
'$translatedName $rank', '$translatedName $rank',

View File

@@ -784,5 +784,4 @@ class _NewCharacterScreenState extends State<NewCharacterScreen> {
ClassPassiveType.firstStrikeBonus => passive.description, ClassPassiveType.firstStrikeBonus => passive.description,
}; };
} }
} }

View File

@@ -11,10 +11,7 @@ import 'package:asciineverdie/src/core/animation/race_character_frames.dart';
/// 새 캐릭터 생성 화면에서 선택한 종족의 idle 애니메이션을 보여줌. /// 새 캐릭터 생성 화면에서 선택한 종족의 idle 애니메이션을 보여줌.
/// RichText 기반 색상 적용. /// RichText 기반 색상 적용.
class RacePreview extends StatefulWidget { class RacePreview extends StatefulWidget {
const RacePreview({ const RacePreview({super.key, required this.raceId});
super.key,
required this.raceId,
});
/// 종족 ID (예: "byte_human", "kernel_giant") /// 종족 ID (예: "byte_human", "kernel_giant")
final String raceId; final String raceId;
@@ -121,9 +118,7 @@ class _RacePreviewState extends State<RacePreview> {
); );
} }
return RichText( return RichText(text: TextSpan(children: spans));
text: TextSpan(children: spans),
);
} }
/// 문자별 색상 매핑 /// 문자별 색상 매핑

View File

@@ -122,10 +122,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Icon(Icons.settings, color: theme.colorScheme.primary), Icon(Icons.settings, color: theme.colorScheme.primary),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(game_l10n.uiSettings, style: theme.textTheme.titleLarge),
game_l10n.uiSettings,
style: theme.textTheme.titleLarge,
),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
@@ -196,9 +193,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
padding: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.only(bottom: 8),
child: Text( child: Text(
title, title,
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(
fontWeight: FontWeight.bold, context,
), ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
), ),
); );
} }
@@ -292,7 +289,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
leading: Text(lang.$3, style: const TextStyle(fontSize: 24)), leading: Text(lang.$3, style: const TextStyle(fontSize: 24)),
title: Text(lang.$2), title: Text(lang.$2),
trailing: isSelected trailing: isSelected
? Icon(Icons.check, color: Theme.of(context).colorScheme.primary) ? Icon(
Icons.check,
color: Theme.of(context).colorScheme.primary,
)
: null, : null,
onTap: () { onTap: () {
game_l10n.setGameLocale(lang.$1); game_l10n.setGameLocale(lang.$1);
@@ -331,16 +331,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [Text(label), Text('$percentage%')],
Text(label),
Text('$percentage%'),
],
),
Slider(
value: value,
onChanged: onChanged,
divisions: 10,
), ),
Slider(value: value, onChanged: onChanged, divisions: 10),
], ],
), ),
), ),