import 'package:flutter/material.dart'; import '../models/pokemon.dart'; import '../components/pokemon_tile.dart'; import '../database/pokedex_database.dart'; import '../api/pokemon_api.dart'; class PokemonListPage extends StatefulWidget { const PokemonListPage({Key? key}) : super(key: key); @override State createState() => _PokemonListPageState(); } class _PokemonListPageState extends State { String _filter = 'ALL'; // ALL, CAUGHT, NEW int _caughtCount = 0; List _allPokemon = []; List _filteredPokemon = []; bool _isSyncing = false; final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); _loadPokemonData(); PokedexDatabase.onDatabaseUpdate.addListener(_loadPokemonData); } @override void dispose() { PokedexDatabase.onDatabaseUpdate.removeListener(_loadPokemonData); _scrollController.dispose(); super.dispose(); } Future _loadPokemonData() async { setState(() => _isSyncing = true); final count = await PokedexDatabase.getCaughtCount(); // Check if database is empty for initial sync List localData = await PokedexDatabase.getPokemonList(); if(localData.isEmpty) { try { final List remoteData = await PokemonApi.getAllPokemon(); // Insert all for (var p in remoteData) { await PokedexDatabase.insertPokemon(p); } localData = await PokedexDatabase.getPokemonList(); } catch (e) { debugPrint(e.toString()); } } // Sort by ID to ensure order localData.sort((a, b) => a.id.compareTo(b.id)); if (mounted) { setState(() { _allPokemon = localData; _caughtCount = count; _applyFilter(); _isSyncing = false; }); } } void _applyFilter() { setState(() { if (_filter == 'ALL') { _filteredPokemon = _allPokemon; } else if (_filter == 'CAUGHT') { _filteredPokemon = _allPokemon.where((p) => p.isCaught).toList(); } }); // Reset scroll position to top when filter changes if (_scrollController.hasClients) { _scrollController.jumpTo(0); } } Widget _buildPokemonTile(BuildContext context, int index) { final pokemon = _filteredPokemon[index]; return PokemonTile(pokemon); } @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( color: Color(0xFFC8D1D8), // Silver-ish grey background ), child: Column( children: [ // Header Container( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), color: const Color(0xFF90A4AE), child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.menu, color: Colors.black87), Text( 'LIST - NATIONAL', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, letterSpacing: 2), ), Icon(Icons.search, color: Colors.black87), ], ), ), // Tabs Container( color: const Color(0xFF90A4AE), height: 40, child: Row( children: [ _buildTab('ALL', _filter == 'ALL'), _buildTab('CAUGHT', _filter == 'CAUGHT'), ], ), ), // Caught Count Bar Container( padding: const EdgeInsets.symmetric(vertical: 12.0), decoration: const BoxDecoration( color: Color(0xFFB0BEC5), border: Border(bottom: BorderSide(color: Color(0xFF78909C), width: 2)), ), child: Column( children: [ Text( '${_caughtCount.toString().padLeft(3, '0')} / ${_allPokemon.length}', style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold), ), const Text( 'POKEMON DISCOVERED', style: TextStyle(fontSize: 14, color: Colors.black54, letterSpacing: 1), ), ], ), ), // The List Expanded( child: Stack( children: [ // Scanlines effect Positioned.fill( child: ListView.builder( itemCount: 100, // drawing artificial scanlines physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => Container( height: 4, margin: const EdgeInsets.only(bottom: 4), color: Colors.black.withAlpha(2), ), ), ), if (_isSyncing && _allPokemon.isEmpty) const Center(child: CircularProgressIndicator()) else if (_filteredPokemon.isEmpty) Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.search_off, size: 64, color: Colors.black26), const SizedBox(height: 16), Text( 'NO POKEMON FOUND IN $_filter', style: const TextStyle(color: Colors.black45, fontSize: 18, fontWeight: FontWeight.bold), ), ], ), ) else ListView.builder( controller: _scrollController, padding: const EdgeInsets.all(12), itemCount: _filteredPokemon.length, itemBuilder: _buildPokemonTile, ), ], ), ), // Footer Container( height: 24, color: const Color(0xFF1B2333), alignment: Alignment.center, child: const Text( 'NATIONAL POKEDEX V2.0', style: TextStyle(color: Colors.white70, fontSize: 12, letterSpacing: 1), ), ), ], ), ); } Widget _buildTab(String title, bool isSelected) { return Expanded( child: GestureDetector( onTap: () { if (_filter != title) { _filter = title; _applyFilter(); } }, child: Container( decoration: BoxDecoration( color: isSelected ? const Color(0xFFB0BEC5) : Colors.transparent, border: isSelected ? const Border( bottom: BorderSide(color: Color(0xFFD32F2F), width: 3), ) : null, ), alignment: Alignment.center, child: Text( title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: isSelected ? Colors.black : Colors.black54, ), ), ), ), ); } }