feat(front): 프론트 화면 개선 및 설정 저장소 추가
- front_screen_animation.dart: 프론트 화면 애니메이션 추가 - settings_repository.dart: 설정 저장소 구현 - front/widgets/: 프론트 화면 위젯 분리 - mobile_carousel_layout.dart: 모바일 레이아웃 개선 - app.dart: 앱 설정 개선 - game_text_l10n.dart: 텍스트 추가
This commit is contained in:
@@ -39,6 +39,8 @@ class GamePlayScreen extends StatefulWidget {
|
||||
required this.controller,
|
||||
this.forceCarouselLayout = false,
|
||||
this.forceDesktopLayout = false,
|
||||
this.onThemeModeChange,
|
||||
this.currentThemeMode = ThemeMode.system,
|
||||
});
|
||||
|
||||
final GameSessionController controller;
|
||||
@@ -49,6 +51,12 @@ class GamePlayScreen extends StatefulWidget {
|
||||
/// 테스트 모드: 모바일에서도 데스크톱 3패널 레이아웃 강제 사용
|
||||
final bool forceDesktopLayout;
|
||||
|
||||
/// 테마 모드 변경 콜백
|
||||
final void Function(ThemeMode mode)? onThemeModeChange;
|
||||
|
||||
/// 현재 테마 모드
|
||||
final ThemeMode currentThemeMode;
|
||||
|
||||
@override
|
||||
State<GamePlayScreen> createState() => _GamePlayScreenState();
|
||||
}
|
||||
@@ -564,9 +572,24 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
},
|
||||
notificationService: _notificationService,
|
||||
specialAnimation: _specialAnimation,
|
||||
onLanguageChange: (locale) {
|
||||
onLanguageChange: (locale) async {
|
||||
// 1. 현재 상태 저장
|
||||
await widget.controller.pause(saveOnStop: true);
|
||||
// 2. 로케일 변경
|
||||
game_l10n.setGameLocale(locale);
|
||||
setState(() {});
|
||||
// 3. 화면 재생성 (전체 UI 재구성)
|
||||
if (context.mounted) {
|
||||
await widget.controller.resume();
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => GamePlayScreen(
|
||||
controller: widget.controller,
|
||||
currentThemeMode: widget.currentThemeMode,
|
||||
onThemeModeChange: widget.onThemeModeChange,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onDeleteSaveAndNewGame: () async {
|
||||
// 게임 루프 중지
|
||||
@@ -578,6 +601,8 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
currentThemeMode: widget.currentThemeMode,
|
||||
onThemeModeChange: widget.onThemeModeChange,
|
||||
),
|
||||
// 사망 오버레이
|
||||
if (state.isDead && state.deathInfo != null)
|
||||
|
||||
@@ -37,6 +37,8 @@ class MobileCarouselLayout extends StatefulWidget {
|
||||
required this.onLanguageChange,
|
||||
required this.onDeleteSaveAndNewGame,
|
||||
this.specialAnimation,
|
||||
this.currentThemeMode = ThemeMode.system,
|
||||
this.onThemeModeChange,
|
||||
});
|
||||
|
||||
final GameState state;
|
||||
@@ -51,6 +53,8 @@ class MobileCarouselLayout extends StatefulWidget {
|
||||
final void Function(String locale) onLanguageChange;
|
||||
final VoidCallback onDeleteSaveAndNewGame;
|
||||
final AsciiAnimationType? specialAnimation;
|
||||
final ThemeMode currentThemeMode;
|
||||
final void Function(ThemeMode mode)? onThemeModeChange;
|
||||
|
||||
@override
|
||||
State<MobileCarouselLayout> createState() => _MobileCarouselLayoutState();
|
||||
@@ -94,6 +98,66 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
||||
return l10n.languageEnglish;
|
||||
}
|
||||
|
||||
/// 현재 테마명 가져오기
|
||||
String _getCurrentThemeName() {
|
||||
return switch (widget.currentThemeMode) {
|
||||
ThemeMode.light => l10n.themeLight,
|
||||
ThemeMode.dark => l10n.themeDark,
|
||||
ThemeMode.system => l10n.themeSystem,
|
||||
};
|
||||
}
|
||||
|
||||
/// 테마 아이콘 가져오기
|
||||
IconData _getThemeIcon() {
|
||||
return switch (widget.currentThemeMode) {
|
||||
ThemeMode.light => Icons.light_mode,
|
||||
ThemeMode.dark => Icons.dark_mode,
|
||||
ThemeMode.system => Icons.brightness_auto,
|
||||
};
|
||||
}
|
||||
|
||||
/// 테마 선택 다이얼로그 표시
|
||||
void _showThemeDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(l10n.menuTheme),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildThemeOption(context, ThemeMode.system, l10n.themeSystem),
|
||||
_buildThemeOption(context, ThemeMode.light, l10n.themeLight),
|
||||
_buildThemeOption(context, ThemeMode.dark, l10n.themeDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildThemeOption(
|
||||
BuildContext context,
|
||||
ThemeMode mode,
|
||||
String label,
|
||||
) {
|
||||
final isSelected = widget.currentThemeMode == mode;
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
color: isSelected ? Theme.of(context).colorScheme.primary : null,
|
||||
),
|
||||
title: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context); // 다이얼로그 닫기
|
||||
widget.onThemeModeChange?.call(mode);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 언어 선택 다이얼로그 표시
|
||||
void _showLanguageDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
@@ -242,6 +306,24 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
||||
},
|
||||
),
|
||||
|
||||
// 테마 변경
|
||||
if (widget.onThemeModeChange != null)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
_getThemeIcon(),
|
||||
color: Colors.purple,
|
||||
),
|
||||
title: Text(l10n.menuTheme),
|
||||
trailing: Text(
|
||||
_getCurrentThemeName(),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_showThemeDialog(context);
|
||||
},
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// 저장
|
||||
|
||||
Reference in New Issue
Block a user