// packages/feature_common/lib/screens/ranking_screen.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:service_api/service_api.dart'; class RankingScreen extends StatefulWidget { // ๐Ÿ”ฝ [์ˆ˜์ •] ์ƒ์„ฑ์ž์—์„œ 3๊ฐœ์˜ ๊ฐ’์„ ์ฃผ์ž…๋ฐ›์Œ final String gameType; // 'SUDOKU' ๋˜๋Š” 'SPIDER' final List difficulties; // ํ‘œ์‹œํ•  ๋‚œ์ด๋„ ๋ชฉ๋ก final String? initialDifficultyName; // ๋žญํ‚น ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ „๋‹ฌ๋œ ์ดˆ๊ธฐ๊ฐ’ const RankingScreen({ super.key, required this.gameType, required this.difficulties, this.initialDifficultyName, }); @override State createState() => _RankingScreenState(); } class _RankingScreenState extends State { final PuzzleService _puzzleService = PuzzleService(); late Future> _rankingFuture; late String _selectedDifficultyName; @override void initState() { super.initState(); // ๐Ÿ”ฝ [์ˆ˜์ •] ์ฃผ์ž…๋ฐ›์€ ๋‚œ์ด๋„ ๋ชฉ๋ก(widget.difficulties)์„ ์‚ฌ์šฉ String defaultDifficultyName = widget.initialDifficultyName ?? widget.difficulties.first.name; if (!widget.difficulties.any((d) => d.name == defaultDifficultyName)) { defaultDifficultyName = widget.difficulties.first.name; } _fetchRanksForDifficulty(defaultDifficultyName); } void _fetchRanksForDifficulty(String difficultyName) { setState(() { _selectedDifficultyName = difficultyName; // ๐Ÿ”ฝ [์ˆ˜์ •] ์„ ํƒ๋œ ์ด๋ฆ„์œผ๋กœ contextId๋ฅผ ์ฐพ์Œ final String contextId = widget.difficulties .firstWhere((d) => d.name == difficultyName) .contextId; // ๐Ÿ”ฝ [์ˆ˜์ •] ์ฃผ์ž…๋ฐ›์€ widget.gameType ์‚ฌ์šฉ _rankingFuture = _puzzleService.fetchRanks(widget.gameType, contextId); }); } String _formatTime(int seconds) { final min = (seconds ~/ 60).toString().padLeft(2, '0'); final sec = (seconds % 60).toString().padLeft(2, '0'); return '$min:$sec'; } String _formatScore(int? storedScore) { int score = 5 - (storedScore ?? 5); return 'SCORE: $score'; } @override Widget build(BuildContext context) { context.watch(); final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: Text('${widget.gameType} ๋žญํ‚น')), body: Column( children: [ // 1. ๋‚œ์ด๋„ ์„ ํƒ Dropdown Padding( padding: const EdgeInsets.all(16.0), child: DropdownButton( value: _selectedDifficultyName, isExpanded: true, // ๐Ÿ”ฝ [์ˆ˜์ •] ์ฃผ์ž…๋ฐ›์€ widget.difficulties๋กœ ๋ฉ”๋‰ด ์ƒ์„ฑ items: widget.difficulties.map((level) { return DropdownMenuItem( value: level.name, child: Text(level.name), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { _fetchRanksForDifficulty(newValue); } }, ), ), // 2. ๋žญํ‚น ๋ฆฌ์ŠคํŠธ (์ดํ•˜ build ๋กœ์ง์€ ์›๋ณธ๊ณผ ๋™์ผ) Expanded( child: FutureBuilder>( future: _rankingFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('๋žญํ‚น ๋กœ๋”ฉ ์‹คํŒจ: ${snapshot.error}')); } if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('๋“ฑ๋ก๋œ ๋žญํ‚น์ด ์—†์Šต๋‹ˆ๋‹ค.')); } final ranks = snapshot.data!; return ListView.builder( itemCount: ranks.length, itemBuilder: (context, index) { final rank = ranks[index]; return ListTile( leading: Text( '${index + 1}.', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), title: Text(rank.playerName, style: const TextStyle(fontSize: 18)), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatTime(rank.primaryScore), style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: theme.primaryColor ), ), Text( _formatScore(rank.secondaryScore), style: TextStyle( fontSize: 12, color: theme.textTheme.bodySmall?.color ), ), ], ), ); }, ); }, ), ), ], ), ); } }