import 'dart:io'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; // For DateFormat import 'package:path_provider/path_provider.dart'; // For getApplicationDocumentsDirectory import 'target_view.dart'; // Import the TargetViewScreen import 'package:shared_preferences/shared_preferences.dart'; import '../utils/translations.dart'; import '../utils/target_colors.dart'; class BookScreen extends StatefulWidget { const BookScreen({super.key}); @override State createState() => _BookScreenState(); } class _BookScreenState extends State with WidgetsBindingObserver { Map> _groupedFiles = {}; // Files grouped by folder Map _folderCreationDates = {}; // Folder creation dates Map _folderScores = {}; // Folder scores (sum of scores of all target files inside) Color _bulletColor = Colors.blue; // default fallback Map _folderTypes = {}; // true = tournament, false = training String _folderFilter = 'all'; bool isRenameEnabled = false; String _sortMode = 'date'; // or 'score' String _selectedLanguage = 'English'; String? _expandedFolder; // Track which folder is expanded late Color _backgroundColor; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); // Register to listen for lifecycle events // Initialize with default target background color _backgroundColor = TargetColors.getBackgroundColor('assets/target.png'); _loadBulletColor(); _loadLanguage(); _loadSelectedTargetColor(); _loadTargetDataFiles(); // Initial load when the screen is first created } String _t(String key) { return AppTranslations.translate(key, _selectedLanguage); } Future _loadLanguage() async { final prefs = await SharedPreferences.getInstance(); setState(() { _selectedLanguage = prefs.getString('language') ?? 'English'; }); } Future _loadSelectedTargetColor() async { final prefs = await SharedPreferences.getInstance(); final selectedTarget = prefs.getString('selectedTarget') ?? 'assets/target.png'; setState(() { _backgroundColor = TargetColors.getBackgroundColor(selectedTarget); }); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); // Unregister from lifecycle events when disposed super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { // Reload data when the screen is resumed or comes into view _loadTargetDataFiles(); } } Future _loadBulletColor() async { final prefs = await SharedPreferences.getInstance(); final selected = prefs.getString('bulletColor') ?? 'Blue'; setState(() { _bulletColor = _getColorFromName(selected); }); } Color _getColorFromName(String colorName) { switch (colorName.toLowerCase()) { case 'blue': return Colors.blue; case 'green': return Colors.green; case 'yellow': return Colors.yellow; case 'purple': return Colors.purple; case 'cyan': return Colors.cyan; case 'teal': return Colors.teal; case 'indigo': return Colors.indigo; case 'pink': return Colors.pink; case 'lime': return Colors.lime; case 'mint': return Colors.greenAccent; case 'orange': return Colors.orange; case 'violet': return Colors.deepPurpleAccent; case 'amber': return Colors.amber; case 'brown': return Colors.brown; case 'grey': return Colors.grey; default: return Colors.blue; // fallback } } // Load the target data files from the directory void _loadTargetDataFiles() async { try { final directory = await getApplicationDocumentsDirectory(); final dataDirectory = Directory('${directory.path}/saved_images'); if (!await dataDirectory.exists()) { await dataDirectory.create(recursive: true); } List folders = dataDirectory.listSync().whereType().toList(); Map> groupedFiles = {}; Map folderDates = {}; Map folderScores = {}; Map folderTypes = {}; for (var folder in folders) { String folderName = folder.path.split('/').last; DateTime fallbackDate = folder.statSync().changed; // fallback // Read metadata final metadataFile = File('${folder.path}/metadata.json'); bool isTournament = false; DateTime? createdAt; if (await metadataFile.exists()) { try { final metadata = jsonDecode(await metadataFile.readAsString()); isTournament = metadata['isTournament'] == true; if (metadata['createdAt'] != null) { createdAt = DateTime.tryParse(metadata['createdAt']); } } catch (e) { debugPrint("Error reading metadata for $folderName: $e"); } } // Use createdAt if available, fallback otherwise folderDates[folderName] = createdAt ?? fallbackDate; folderTypes[folderName] = isTournament; // List target files List targetFiles = Directory(folder.path) .listSync() .whereType() .where((file) => file.path.endsWith(".json") && !file.path.endsWith("metadata.json")) .toList(); int totalScore = 0; for (var file in targetFiles) { final jsonString = await file.readAsString(); final data = jsonDecode(jsonString); totalScore += _getScoreFromJson(data); } groupedFiles[folderName] = targetFiles; folderScores[folderName] = totalScore; } // Sort folders by stored or fallback created date (DESC) final sortedFolderNames = folderDates.keys.toList() ..sort((a, b) => folderDates[b]!.compareTo(folderDates[a]!)); setState(() { _groupedFiles = { for (var name in sortedFolderNames) name: groupedFiles[name]! }; _folderCreationDates = { for (var name in sortedFolderNames) name: folderDates[name]! }; _folderScores = { for (var name in sortedFolderNames) name: folderScores[name]! }; _folderTypes = { for (var name in sortedFolderNames) name: folderTypes[name]! }; }); } catch (e) { debugPrint("Error loading target data files: $e"); } } // Function to safely get the score from the JSON data int _getScoreFromJson(Map data) { if (data.containsKey('totalScore') && data['totalScore'] is int) { return data['totalScore']; } return 0; // Default to 0 if the score is not present or invalid } // Delete a file Future _deleteFile(File file) async { bool? confirmDeletion = await _showConfirmationDialog( context, _t('Are you sure you want to delete this file?'), ); if (confirmDeletion == true) { try { await file.delete(); _loadTargetDataFiles(); // Refresh after deleting the file } catch (e) { debugPrint("Error deleting file: $e"); } } } // Open file preview in the TargetViewScreen void _openFilePreview(File targetFile) { Navigator.push( context, MaterialPageRoute( builder: (context) => TargetViewScreen( jsonFiles: [targetFile], // Pass the selected file in a list refreshFolders: _loadTargetDataFiles, // Pass the refreshFolders callback ), ), ); } // Open folder's combined target view in the TargetViewScreen void _openFolderTargetView(String folderName) async { final targetFiles = _groupedFiles[folderName] ?? []; List> allBullets = []; String latestDate = 'Unknown'; for (var file in targetFiles) { final jsonString = await file.readAsString(); final data = jsonDecode(jsonString); final bullets = (data['bullets'] as List) .map((b) => {'x': b['x'], 'y': b['y']}) .toList(); allBullets.addAll(bullets); if (data['date'] != null) { latestDate = data['date']; // You could sort if you want the most recent } } // Create combined JSON structure final combinedJson = jsonEncode({ 'date': latestDate, 'bullets': allBullets, }); // Save this JSON temporarily to disk final tempDir = await getTemporaryDirectory(); final combinedFile = File('${tempDir.path}/combined_${folderName}.json'); await combinedFile.writeAsString(combinedJson); // Navigate to view screen with the combined file Navigator.push( context, MaterialPageRoute( builder: (context) => TargetViewScreen( jsonFiles: [combinedFile], // treat it like a normal file refreshFolders: _loadTargetDataFiles, isCombinedView: true, // 👈 This line ), ), ); } List _filteredFolderNames() { final List folders = _groupedFiles.keys.toList(); if (_folderFilter != 'all') { final isTournament = _folderFilter == 'tournament'; folders.retainWhere((folderName) => _folderTypes[folderName] == isTournament); } // 🔽 Sort using `createdAt` from _folderCreationDates (which is now based on metadata.json) // 🔽 Apply sorting based on selected sort mode folders.sort((a, b) { if (_sortMode == 'score') { final scoreA = _folderScores[a] ?? 0; final scoreB = _folderScores[b] ?? 0; return scoreB.compareTo(scoreA); // High score first } else { final dateA = _folderCreationDates[a]; final dateB = _folderCreationDates[b]; if (dateA == null || dateB == null) return 0; return dateB.compareTo(dateA); // Newest first } }); return folders; } Future _renameFolder(String oldFolderName) async { final newNameController = TextEditingController(); bool isTournament = _folderTypes[oldFolderName] == true; final result = await showDialog>( context: context, builder: (context) { return Dialog( backgroundColor: Colors.grey[900], shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(24.0), child: StatefulBuilder( builder: (context, setState) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( _t('Folder Settings'), style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), TextField( controller: newNameController, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white), decoration: InputDecoration( filled: true, fillColor: Colors.grey[800], hintText: _t('Enter new folder name'), hintStyle: const TextStyle(color: Colors.white54), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: _bulletColor), ), ), onChanged: (value) { setState(() { isRenameEnabled = value.trim().isNotEmpty; }); }, ), const SizedBox(height: 16), GestureDetector( onTap: () { setState(() { isTournament = !isTournament; }); }, child: Container( width: double.infinity, decoration: BoxDecoration( color: isTournament ? _bulletColor.withValues(alpha: 0.2) : Colors.grey[850], borderRadius: BorderRadius.circular(12), border: Border.all( color: isTournament ? _bulletColor : Colors.grey[700]!, width: 1.5, ), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.emoji_events, size: 16, color: isTournament ? _bulletColor : Colors.white70, ), const SizedBox(width: 8), Text( isTournament ? _t('Tournament Mode') : _t('Mark as Tournament'), style: TextStyle( color: isTournament ? _bulletColor : Colors.white70, fontWeight: FontWeight.w500, fontSize: 16, ), ), ], ), ), ), ), const SizedBox(height: 20), Row( children: [ Expanded( child: ElevatedButton( onPressed: () => Navigator.of(context).pop(), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[800], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text(_t('Cancel')), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () => Navigator.of(context).pop({ 'name': newNameController.text.trim(), 'isTournament': isTournament, }), style: ElevatedButton.styleFrom( backgroundColor: _bulletColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text(_t('Apply')), ), ), ], ) ], ); }, ), ), ); }, ); // ✅ Handle result (rename + tag update) if (result != null) { final newName = result['name']; final isTournament = result['isTournament'] ?? false; final directory = await getApplicationDocumentsDirectory(); final oldFolderPath = Directory('${directory.path}/saved_images/$oldFolderName'); final targetFolderName = (newName != null && newName.isNotEmpty) ? newName : oldFolderName; final newFolderPath = Directory('${directory.path}/saved_images/$targetFolderName'); if (targetFolderName != oldFolderName) { if (await newFolderPath.exists()) { debugPrint('A folder named "$targetFolderName" already exists.'); return; } else { await oldFolderPath.rename(newFolderPath.path); } } // ✅ Preserve existing createdAt value or set it if missing final metadataFile = File('${newFolderPath.path}/metadata.json'); String? createdAt; if (await metadataFile.exists()) { try { final existing = jsonDecode(await metadataFile.readAsString()); createdAt = existing['createdAt']; } catch (_) {} } createdAt ??= DateTime.now().toIso8601String(); // fallback if not found final updatedMetadata = { 'isTournament': isTournament, 'createdAt': createdAt, }; await metadataFile.writeAsString(jsonEncode(updatedMetadata)); _loadTargetDataFiles(); // Refresh UI } } // Delete a folder Future _deleteFolder(String folderName) async { bool? confirmDeletion = await _showConfirmationDialog( context, '${_t('Are you sure you want to delete the folder')} "$folderName" ${_t('and all of its contents?')}', ); if (confirmDeletion == true) { try { final directory = await getApplicationDocumentsDirectory(); final folderPath = Directory('${directory.path}/saved_images/$folderName'); if (await folderPath.exists()) { await folderPath.delete(recursive: true); } _loadTargetDataFiles(); // Refresh file/folder list } catch (e) { debugPrint("Error deleting folder: $e"); } } } Future _showConfirmationDialog(BuildContext context, String message) async { return showDialog( context: context, builder: (BuildContext context) { return Dialog( backgroundColor: Colors.grey[900], shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( _t('Confirm Deletion'), style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), const SizedBox(height: 16), Text( message, style: const TextStyle(color: Colors.white70, fontSize: 16), textAlign: TextAlign.center, ), const SizedBox(height: 24), // "Yes" Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( backgroundColor: Colors.redAccent, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text(_t("Yes, delete")), ), ), const SizedBox(height: 12), // "No" Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.of(context).pop(false), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[800], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text(_t("Cancel")), ), ), ], ), ), ); }, ); } Widget _buildFolderRow(String folderName) { final numberOfTargets = _groupedFiles[folderName]!.length; final totalScore = _folderScores[folderName]; final folderDate = _folderCreationDates[folderName]; final formattedDate = folderDate != null ? DateFormat('dd/MM/yyyy').format(folderDate) : 'Unknown Date'; return Container( margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(12), ), child: Column( children: [ // Header section with grey background like toolbars InkWell( onTap: () { setState(() { _expandedFolder = _expandedFolder == folderName ? null : folderName; }); }, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), child: Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.grey[900], borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ // White indicator line Container( width: 4, height: 20, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 12), Row( children: [ if (_folderTypes[folderName] == true) ...[ const Icon(Icons.emoji_events, size: 18, color: Colors.white), const SizedBox(width: 8), ], Text( folderName, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), ), ], ), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.calendar_today, size: 14, color: Colors.white), const SizedBox(width: 6), Text( formattedDate, style: const TextStyle(fontSize: 13, color: Colors.white), ), const SizedBox(width: 12), Icon( _expandedFolder == folderName ? Icons.expand_less : Icons.expand_more, color: Colors.white, ), ], ), ], ), ), ), // Expandable targets list (in the middle) if (_expandedFolder == folderName) ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[100], ), child: _buildTargetsList(folderName), ), ], // Bottom section: Info badge and action buttons IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Info badge section (non-clickable, matches button style) Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.only( bottomLeft: const Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // Targets count Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.filter_center_focus, size: 16, color: Colors.black87), const SizedBox(width: 4), Text( "$numberOfTargets", style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black87), ), ], ), // Score Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.emoji_events, size: 16, color: Colors.black87), const SizedBox(width: 4), Text( "$totalScore", style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black87), ), ], ), ], ), ), ), // Preview button section InkWell( onTap: () => _openFolderTargetView(folderName), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), decoration: BoxDecoration( color: _bulletColor.withValues(alpha: 0.2), border: Border( left: BorderSide(color: Colors.grey[400]!, width: 1), ), ), child: Icon(Icons.visibility, color: _bulletColor, size: 20), ), ), // Edit button section InkWell( onTap: () => _renameFolder(folderName), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), decoration: BoxDecoration( color: _bulletColor.withValues(alpha: 0.2), border: Border( left: BorderSide(color: Colors.grey[400]!, width: 1), ), ), child: Icon(Icons.edit, color: _bulletColor, size: 20), ), ), // Delete button section InkWell( onTap: () => _deleteFolder(folderName), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), decoration: BoxDecoration( color: Colors.red.withValues(alpha: 0.2), borderRadius: const BorderRadius.only( bottomRight: Radius.circular(12), ), border: Border( left: BorderSide(color: Colors.grey[400]!, width: 1), ), ), child: const Icon(Icons.delete, color: Colors.red, size: 20), ), ), ], ), ), ], ), ); } Widget _buildTargetsList(String folderName) { final targetFiles = _groupedFiles[folderName] ?? []; if (targetFiles.isEmpty) { return Padding( padding: const EdgeInsets.all(16.0), child: Text( _t("No targets in this folder"), style: const TextStyle(color: Colors.black54, fontSize: 14), textAlign: TextAlign.center, ), ); } return Column( children: targetFiles.asMap().entries.map((entry) { final index = entry.key; final file = entry.value; final score = _getScoreFromJson(jsonDecode(file.readAsStringSync())); final fileName = "${_t('Target')} ${index + 1}"; return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey[300]!, width: 1), ), child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Target info section - Clickable Expanded( child: InkWell( onTap: () => _openFilePreview(file), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), child: Row( children: [ Expanded( child: Text( fileName, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, ), ), ), const SizedBox(width: 8), const Icon( Icons.emoji_events, color: Colors.black87, size: 14, ), const SizedBox(width: 4), Text( "$score", style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ], ), ), ), ), // Delete button InkWell( onTap: () => _deleteFile(file), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: Colors.red.withValues(alpha: 0.1), border: Border( left: BorderSide(color: Colors.grey[300]!, width: 1), ), borderRadius: const BorderRadius.only( topRight: Radius.circular(10), bottomRight: Radius.circular(10), ), ), child: const Icon( Icons.delete, color: Colors.red, size: 18, ), ), ), ], ), ), ); }).toList(), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: _backgroundColor, body: Column( children: [ // 🔽 Filter dropdown at the top (styled like tab bar) Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: Colors.grey[900], border: Border( bottom: BorderSide(color: Colors.grey[800]!, width: 1), ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: GestureDetector( onTap: () => _showFolderFilterPopup(context), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Icon( _folderFilter == 'all' ? Icons.folder : _folderFilter == 'tournament' ? Icons.emoji_events : Icons.track_changes, size: 16, color: Colors.black, ), const SizedBox(width: 6), Text( _folderFilter == 'all' ? _t('Showing: All folders') : _folderFilter == 'tournament' ? _t('Showing: Tournament folders') : _t('Showing: Training folders'), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black), ), ], ), Icon(Icons.filter_list, color: Colors.black87, size: 18), ], ), ), ), ), ), // 📁 Folder list or empty state Expanded( child: _groupedFiles.isEmpty ? Center(child: Text(_t("No saved target data found"), style: const TextStyle(fontSize: 18))) : Scrollbar( thumbVisibility: true, // always show scrollbar thickness: 6, // optional: adjust width radius: const Radius.circular(8), child: ListView.builder( padding: const EdgeInsets.only(top: 8), itemCount: _filteredFolderNames().length, itemBuilder: (context, index) { String folderName = _filteredFolderNames()[index]; return _buildFolderRow(folderName); }, ), ), ), ], ), ); } void _showFolderFilterPopup(BuildContext context) { showDialog( context: context, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { return Dialog( backgroundColor: Colors.grey[900], shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( _t('Filter Folders'), style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 24), // Folder type filters _buildStyledFilterButton('all', Icons.folder, _t('All folders'), context), const SizedBox(height: 12), _buildStyledFilterButton('tournament', Icons.emoji_events, _t('Tournament'), context), const SizedBox(height: 12), _buildStyledFilterButton('training', Icons.track_changes, _t('Training'), context), const SizedBox(height: 24), Text( _t('Sort By'), style: const TextStyle(color: Colors.white70, fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: GestureDetector( onTap: () { setState(() => _sortMode = 'date'); this.setState(() {}); // 👈 refresh parent widget }, child: Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), margin: const EdgeInsets.only(right: 6), decoration: BoxDecoration( color: _sortMode == 'date' ? _bulletColor : Colors.grey[800], borderRadius: BorderRadius.circular(12), ), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.calendar_today, size: 14, color: _sortMode == 'date' ? Colors.white : Colors.white70, ), const SizedBox(width: 4), Text( _t('Sort by Date'), textAlign: TextAlign.center, style: TextStyle( color: _sortMode == 'date' ? Colors.white : Colors.white70, fontWeight: FontWeight.w500, fontSize: 14, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ), ), ), Expanded( child: GestureDetector( onTap: () { setState(() => _sortMode = 'score'); this.setState(() {}); // 👈 refresh parent widget }, child: Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), margin: const EdgeInsets.only(left: 6), decoration: BoxDecoration( color: _sortMode == 'score' ? _bulletColor : Colors.grey[800], borderRadius: BorderRadius.circular(12), ), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.emoji_events, size: 14, color: _sortMode == 'score' ? Colors.white : Colors.white70, ), const SizedBox(width: 4), Text( _t('Sort by Score'), textAlign: TextAlign.center, style: TextStyle( color: _sortMode == 'score' ? Colors.white : Colors.white70, fontWeight: FontWeight.w500, fontSize: 14, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ), ), ), ], ), ], ), ), ); }, ); }, ); } Widget _buildStyledFilterButton(String value, IconData icon, String label, BuildContext context) { final isSelected = _folderFilter == value; return SizedBox( width: double.infinity, child: GestureDetector( onTap: () { setState(() { _folderFilter = value; }); Navigator.of(context).pop(); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 14), decoration: BoxDecoration( color: isSelected ? _bulletColor : Colors.grey[800], borderRadius: BorderRadius.circular(12), ), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: 18, color: isSelected ? Colors.white : Colors.white70, ), const SizedBox(width: 8), Text( label, style: TextStyle( color: isSelected ? Colors.white : Colors.white70, fontSize: 16, fontWeight: FontWeight.w500, ), ), ], ), ), ), ), ); } }