import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:anyway/core/constants.dart'; import 'package:anyway/presentation/pages/new_trip_preferences.dart'; class TripLocationSelectionPage extends StatefulWidget { const TripLocationSelectionPage({ super.key, this.autoNavigateToPreferences = true, }); final bool autoNavigateToPreferences; @override State createState() => _TripLocationSelectionPageState(); } class _TripLocationSelectionPageState extends State { final CameraPosition _initialCameraPosition = const CameraPosition( // TODO - maybe Paris is not the best default? target: LatLng(48.8566, 2.3522), zoom: 11.0, ); GoogleMapController? _mapController; LatLng? _selectedLocation; bool _useLocation = true; bool _loadingPreferences = true; bool _isSearchingAddress = false; final TextEditingController _searchController = TextEditingController(); static const Map> _debugLocations = { 'paris': [48.8575, 2.3514], 'london': [51.5074, -0.1278], 'new york': [40.7128, -74.006], 'tokyo': [35.6895, 139.6917], }; @override void initState() { super.initState(); _loadLocationPreference(); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _loadLocationPreference() async { final prefs = await SharedPreferences.getInstance(); final useLocation = prefs.getBool('useLocation') ?? true; if (!mounted) return; setState(() { _useLocation = useLocation; _loadingPreferences = false; }); } void _onLongPress(LatLng location) { setState(() { _selectedLocation = location; }); _mapController?.animateCamera(CameraUpdate.newLatLng(location)); } void _setSelectedLocationFromCoords(double lat, double lng) { final latLng = LatLng(lat, lng); setState(() { _selectedLocation = latLng; }); _mapController?.animateCamera(CameraUpdate.newLatLng(latLng)); } Future _useCurrentLocation() async { try { var permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); } if (permission == LocationPermission.denied) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Location permission denied.')), ); return; } if (permission == LocationPermission.deniedForever) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Location permission permanently denied.'), ), ); return; } final position = await Geolocator.getCurrentPosition(); if (!mounted) return; _setSelectedLocationFromCoords(position.latitude, position.longitude); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Unable to get current location: $e')), ); } } Future _searchForLocation(String rawQuery) async { final query = rawQuery.trim(); if (query.isEmpty) { return; } setState(() => _isSearchingAddress = true); try { List locations = []; if (GeocodingPlatform.instance != null) { locations = await locationFromAddress(query); } Location? selected; if (locations.isNotEmpty) { selected = locations.first; } else { final fallback = _debugLocations[query.toLowerCase()]; if (fallback != null) { selected = Location( latitude: fallback[0], longitude: fallback[1], timestamp: DateTime.now(), ); } } if (selected == null) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('No results for "$query".'))); return; } if (!mounted) return; _setSelectedLocationFromCoords(selected.latitude, selected.longitude); } catch (e) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Search failed: $e'))); } finally { if (mounted) { setState(() => _isSearchingAddress = false); } } } Set get _markers => _selectedLocation == null ? {} : { Marker( markerId: const MarkerId('new-trip-start'), position: _selectedLocation!, infoWindow: const InfoWindow(title: 'Trip start'), ), }; void _confirmLocation() { if (_selectedLocation == null) return; final startLocation = [ _selectedLocation!.latitude, _selectedLocation!.longitude, ]; if (!widget.autoNavigateToPreferences) { Navigator.of(context).pop(startLocation); return; } Navigator.of(context).push( MaterialPageRoute( builder: (_) => NewTripPreferencesPage(startLocation: startLocation), ), ); } @override Widget build(BuildContext context) { if (_loadingPreferences) { return Scaffold( appBar: AppBar(title: const Text('Choose Start Location')), body: const Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar(title: const Text('Choose Start Location')), body: Stack( children: [ GoogleMap( onMapCreated: (controller) => _mapController = controller, initialCameraPosition: _initialCameraPosition, onLongPress: _onLongPress, markers: _markers, cloudMapId: MAP_ID, mapToolbarEnabled: false, zoomControlsEnabled: false, myLocationButtonEnabled: false, myLocationEnabled: _useLocation, ), Positioned( top: 16, left: 16, right: 16, child: Column( children: [ SearchBar( controller: _searchController, hintText: 'Enter a city or long-press on the map', leading: const Icon(Icons.search), onSubmitted: _searchForLocation, trailing: [ IconButton( icon: _isSearchingAddress ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.send), onPressed: _isSearchingAddress ? null : () => _searchForLocation(_searchController.text), ), ], ), const SizedBox(height: 8), if (_useLocation) Align( alignment: Alignment.centerLeft, child: TextButton.icon( icon: const Icon(Icons.my_location), label: const Text('Use current location'), onPressed: _useCurrentLocation, ), ), Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(top: 8.0), child: Text( _selectedLocation == null ? 'Long-press anywhere to drop the starting point.' : 'Selected: ${_selectedLocation!.latitude.toStringAsFixed(4)}, ${_selectedLocation!.longitude.toStringAsFixed(4)}', style: Theme.of(context).textTheme.bodyMedium, ), ), ), ], ), ), ], ), floatingActionButton: _selectedLocation == null ? null : FloatingActionButton.extended( onPressed: _confirmLocation, icon: widget.autoNavigateToPreferences ? const Icon(Icons.tune) : const Icon(Icons.check), label: Text( widget.autoNavigateToPreferences ? 'Select Preferences' : 'Use this location', ), ), ); } }