import 'package:flutter/material.dart'; import 'package:wisata_app/models/destination.dart'; import '../services/destination_service.dart'; import '../widgets/app_logo.dart'; import '../widgets/category_filter_chip.dart'; import '../widgets/destination_card.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); static const routeName = '/home'; @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { final DestinationService _destinationService = const DestinationService(); final TextEditingController _searchController = TextEditingController(); final List _categories = const [ 'Semua Wisata', 'Gunung', 'Pantai', 'Danau', 'Air Terjun', ]; String _selectedCategory = 'Semua Wisata'; int _currentIndex = 0; bool _loadingDestinations = true; String? _destinationError; List _allDestinations = []; List get _filteredDestinations { final keyword = _searchController.text.trim().toLowerCase(); return _allDestinations.where((destination) { final matchesCategory = _selectedCategory == 'Semua Wisata' || destination.category.toLowerCase() == _selectedCategory.toLowerCase(); final matchesSearch = keyword.isEmpty || destination.title.toLowerCase().contains(keyword) || destination.shortDescription.toLowerCase().contains(keyword) || destination.location.toLowerCase().contains(keyword); return matchesCategory && matchesSearch; }).toList(); } @override void initState() { super.initState(); _searchController.addListener(() => setState(() {})); _loadDestinations(); } Future _loadDestinations() async { setState(() { _loadingDestinations = true; _destinationError = null; }); try { final destinations = await _destinationService.getAllDestinations(); if (!mounted) return; setState(() { _allDestinations = destinations; _loadingDestinations = false; }); } on Exception catch (error) { if (!mounted) return; setState(() { _destinationError = error.toString(); _loadingDestinations = false; }); } } @override void dispose() { _searchController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; return Scaffold( backgroundColor: const Color(0xFFF4F8FB), appBar: AppBar( title: const AppLogo(size: 42, showText: true), actions: const [SizedBox(width: 14)], ), body: RefreshIndicator( onRefresh: _loadDestinations, child: ListView( padding: const EdgeInsets.fromLTRB(20, 12, 20, 110), children: [ Text( 'Temukan Petualangan Wisata Berikutnya', style: Theme.of(context).textTheme.displaySmall?.copyWith( color: const Color(0xFF0B1F33), fontWeight: FontWeight.w900, ), ), const SizedBox(height: 18), TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Cari wisata favoritmu...', prefixIcon: const Icon(Icons.search_rounded), suffixIcon: _searchController.text.isNotEmpty ? IconButton( onPressed: _searchController.clear, icon: const Icon(Icons.close_rounded), ) : null, ), ), const SizedBox(height: 18), SizedBox( height: 52, child: ListView.separated( scrollDirection: Axis.horizontal, itemBuilder: (context, index) { final category = _categories[index]; return CategoryFilterChip( label: category, icon: _categoryIcon(category), selected: category == _selectedCategory, onSelected: (_) => setState(() => _selectedCategory = category), ); }, separatorBuilder: (_, __) => const SizedBox(width: 10), itemCount: _categories.length, ), ), const SizedBox(height: 26), Row( children: [ Text( 'Destinasi Unggulan', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w900, ), ), const Spacer(), Text( '${_filteredDestinations.length} wisata', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: colors.primary, fontWeight: FontWeight.w800, ), ), ], ), const SizedBox(height: 16), if (_loadingDestinations) const Padding( padding: EdgeInsets.symmetric(vertical: 40), child: Center(child: CircularProgressIndicator()), ) else if (_destinationError != null) _DestinationMessage( icon: Icons.cloud_off_rounded, title: 'Data wisata belum bisa dimuat', message: _destinationError!, ) else if (_filteredDestinations.isEmpty) const _DestinationMessage( icon: Icons.search_off_rounded, title: 'Wisata tidak ditemukan', message: 'Coba ubah pencarian atau filter kategori.', ) else ..._filteredDestinations.map( (destination) => DestinationCard( destination: destination, onTap: () => Navigator.pushNamed( context, '/detail', arguments: destination, ), ), ), ], ), ), bottomNavigationBar: NavigationBar( selectedIndex: _currentIndex, onDestinationSelected: (index) => setState(() => _currentIndex = index), destinations: const [ NavigationDestination( icon: Icon(Icons.home_rounded), label: 'Beranda'), NavigationDestination(icon: Icon(Icons.map_rounded), label: 'Peta'), NavigationDestination( icon: Icon(Icons.person_rounded), label: 'Profil'), ], ), ); } IconData _categoryIcon(String category) { switch (category) { case 'Gunung': return Icons.terrain_rounded; case 'Pantai': return Icons.beach_access_rounded; case 'Danau': return Icons.water_rounded; case 'Air Terjun': return Icons.waterfall_chart_rounded; default: return Icons.travel_explore_rounded; } } } class _DestinationMessage extends StatelessWidget { const _DestinationMessage({ required this.icon, required this.title, required this.message, }); final IconData icon; final String title; final String message; @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(vertical: 40), child: Column( children: [ Icon(icon, size: 48, color: colors.onSurfaceVariant), const SizedBox(height: 12), Text( title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w900, ), ), const SizedBox(height: 6), Text( message, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: colors.onSurfaceVariant, ), ), ], ), ); } }