import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:latlong2/latlong.dart'; import '../../../map/providers/map_provider.dart'; import '../../data/search_repository.dart'; import '../../providers/search_provider.dart'; import '../widgets/search_result_tile.dart'; class SearchScreen extends ConsumerStatefulWidget { const SearchScreen({super.key}); @override ConsumerState createState() => _SearchScreenState(); } class _SearchScreenState extends ConsumerState { late final TextEditingController _controller; @override void initState() { super.initState(); _controller = TextEditingController(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final searchState = ref.watch(searchProvider); return Scaffold( appBar: AppBar( title: TextField( controller: _controller, autofocus: true, decoration: const InputDecoration( hintText: 'Search places...', border: InputBorder.none, filled: false, ), onChanged: (value) { ref.read(searchProvider.notifier).updateQuery(value); }, ), actions: [ if (_controller.text.isNotEmpty) IconButton( icon: const Icon(Icons.clear), onPressed: () { _controller.clear(); ref.read(searchProvider.notifier).updateQuery(''); }, ), ], ), body: _buildBody(context, searchState), ); } Widget _buildBody(BuildContext context, SearchState searchState) { if (searchState.error != null) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 48), const SizedBox(height: 8), Text(searchState.error!), ], ), ); } if (searchState.isLoading) { return const Center(child: CircularProgressIndicator()); } if (searchState.query.trim().isNotEmpty) { if (searchState.results.isEmpty) { return const Center(child: Text('No results found.')); } return ListView.builder( itemCount: searchState.results.length, itemBuilder: (context, index) { final result = searchState.results[index]; return SearchResultTile( result: result, onTap: () => _onResultTap(result), ); }, ); } if (searchState.recentSearches.isEmpty) { return const Center( child: Text('Start typing to search for places.'), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Recent Searches', style: Theme.of(context).textTheme.titleSmall, ), TextButton( onPressed: () => ref.read(searchProvider.notifier).clearHistory(), child: const Text('Clear all'), ), ], ), ), Expanded( child: ListView.builder( itemCount: searchState.recentSearches.length, itemBuilder: (context, index) { final item = searchState.recentSearches[index]; return ListTile( leading: const Icon(Icons.history), title: Text(item.query), trailing: IconButton( icon: const Icon(Icons.close, size: 18), onPressed: () => ref .read(searchProvider.notifier) .deleteHistoryEntry(item.id), ), onTap: () { _controller.text = item.query; ref.read(searchProvider.notifier).updateQuery(item.query); }, ); }, ), ), ], ); } void _onResultTap(SearchResult result) { ref.read(searchProvider.notifier).selectResult(result); ref.read(mapProvider.notifier).selectPlace( SelectedPlace( name: result.name, address: result.displayAddress, category: result.type, latitude: result.latitude, longitude: result.longitude, osmId: result.osmId, osmType: result.osmType, ), ); ref.read(mapProvider.notifier).updateCamera( LatLng(result.latitude, result.longitude), 15, ); context.go('/'); } }