flutter_sudoku/lib/screens/home_screen.dart
2025-11-11 14:38:15 +09:00

188 lines
7.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:sudoku_app/models/game_level.dart'; // 👈 [추가]
import 'package:sudoku_app/models/sudoku_game_dto.dart';
import 'package:sudoku_app/models/sudoku_theme.dart';
import 'package:sudoku_app/screens/game_screen.dart';
import 'package:sudoku_app/screens/ranking_screen.dart';
import 'package:sudoku_app/services/puzzle_service.dart';
import 'package:sudoku_app/services/identity_service.dart';
import 'package:sudoku_app/widgets/ad_banner_widget.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// '최대 잠금 해제 레벨' 상태
int _maxUnlockedLevel = 1;
late String _selectedThemeName;
bool _isLoading = false;
final PuzzleService _puzzleService = PuzzleService();
final IdentityService _identityService = IdentityService();
@override
void initState() {
super.initState();
_selectedThemeName = AppThemes.random;
_loadProgress(); // 👈 저장된 진행 상황 로드
}
// 로컬 저장소에서 클리어 레벨 불러오기
Future<void> _loadProgress() async {
final maxLevel = await _identityService.getMaxUnlockedLevel();
if (mounted) {
setState(() {
_maxUnlockedLevel = maxLevel;
});
}
}
// 게임 시작 로직
Future<void> _startGame(GameLevel level) async {
setState(() { _isLoading = true; });
try {
// 1. 선택한 레벨의 '인덱스'(1-11)를 문자열로 전달
final String difficulty = level.levelIndex.toString();
final SudokuGameDto gameData = await _puzzleService.startGame(difficulty);
final String userId = await _identityService.getOrCreateUserId();
final String? userName = await _identityService.getSavedUserName();
if (mounted) {
// 2. GameScreen으로 이동 (게임 클리어 후 돌아오면 _loadProgress() 호출)
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GameScreen(
gameData: gameData,
themeName: _selectedThemeName, // 👈 '랜덤' 또는 '과일' 등
userId: userId,
userName: userName,
levelIndex: level.levelIndex, // 👈 1~11
),
),
);
// 3. GameScreen에서 돌아왔을 때, 진행 상황(클리어)을 다시 로드
_loadProgress();
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('게임 로딩 실패: $e')),
);
}
} finally {
if (mounted) {
setState(() { _isLoading = false; });
}
}
}
@override
Widget build(BuildContext context) {
// 99 = 모든 레벨 클리어
final bool allLevelsUnlocked = _maxUnlockedLevel >= 99;
return Scaffold(
appBar: AppBar(title: const Text('스도쿠 게임')),
body: LayoutBuilder(
builder: (context, constraints) {
const double maxContentRatio = 0.6;
// 🔽 [수정] 태블릿 등에서 너무 커지는 것을 방지 (최대 500px)
final double constrainedWidth = (constraints.maxHeight * maxContentRatio) > 500
? 500 : (constraints.maxHeight * maxContentRatio);
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: constrainedWidth),
child: Column(
children: [
// 1. 테마 선택
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("테마: ", style: TextStyle(fontSize: 18)),
DropdownButton<String>(
value: _selectedThemeName,
items: AppThemes.selectableThemeNames.map((themeName) {
return DropdownMenuItem<String>(
value: themeName,
child: Text(themeName, style: const TextStyle(fontSize: 20)),
);
}).toList(),
onChanged: (themeName) {
if (themeName != null) {
setState(() { _selectedThemeName = themeName; });
}
},
),
],
),
),
// 2. 🔽 [수정] 레벨 선택 리스트 (Slider 대체)
Expanded(
child: ListView.builder(
itemCount: AppLevels.allLevels.length,
itemBuilder: (context, index) {
final GameLevel level = AppLevels.allLevels[index];
// 3. 잠금 해제 로직
final bool isUnlocked = allLevelsUnlocked || level.levelIndex <= _maxUnlockedLevel;
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: ListTile(
leading: Icon(
isUnlocked ? Icons.lock_open_rounded : Icons.lock_rounded,
color: isUnlocked ? Colors.blue : Colors.grey,
),
title: Text(level.name, style: TextStyle(
fontSize: 18,
fontWeight: isUnlocked ? FontWeight.bold : FontWeight.normal,
color: isUnlocked ? Colors.black : Colors.grey,
)),
trailing: isUnlocked ? const Icon(Icons.play_arrow_rounded) : null,
onTap: isUnlocked && !_isLoading
? () => _startGame(level)
: null, // 잠겼거나 로딩 중이면 탭 비활성화
),
);
},
),
),
// 3. 랭킹 보기 버튼
TextButton(
onPressed: () {
// 현재 최고 레벨을 랭킹 화면의 기본값으로 전달
final String currentDifficultyName = AppLevels.getLevel(_maxUnlockedLevel).name;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RankingScreen(
initialDifficultyName: currentDifficultyName,
),
),
);
},
child: const Text('전체 랭킹 보기'),
),
],
),
),
);
},
),
bottomNavigationBar: const AdBannerWidget(), // 👈 [수정] body -> bottomNavigationBar
);
}
}