fix: LOC — location filter never applied to event API calls

Root cause: SearchScreen popped with a plain city label string; the
pincode was available in search results but discarded. home_screen only
saved the display label to prefs and never updated the 'pincode' key,
so every API call always sent {pincode:'all'} regardless of selection.

GPS path had the same issue — lat/lng were obtained but thrown away
after reverse-geocoding; only the label was passed back.

Fix:
- SearchScreen now pops with Map<String,dynamic> {label, pincode,
  lat?, lng?} instead of a plain String
- Pincode results return their pincode; GPS returns actual coordinates;
  popular city chips look up the first matching pincode from the
  Kerala pincodes DB (fallback 'all' if not found)
- home_screen._openLocationSearch() saves pincode + lat/lng to prefs
  and updates _pincode/_userLat/_userLng in state
- home_screen._loadUserDataAndEvents() prefers getEventsByLocation
  (haversine) when GPS coords are saved, falls back to getEventsByPincode
- EventsService gains getEventsByLocation(lat, lng) which sends
  latitude/longitude/radius_km to the existing Django haversine endpoint
  and auto-expands radius 10→25→50→100 km until ≥ 6 events found

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 18:43:02 +05:30
parent 692f96bfce
commit 34d6586afa
3 changed files with 101 additions and 15 deletions

View File

@@ -106,8 +106,24 @@ class _SearchScreenState extends State<SearchScreen> {
});
}
/// Pop with a structured result so home_screen can update both the display
/// label AND the pincode / GPS coordinates used for API filtering.
void _selectWithPincode(String label, {String? pincode}) {
Navigator.of(context).pop(<String, dynamic>{
'label': label,
'pincode': pincode ?? 'all',
});
}
void _selectAndClose(String location) {
Navigator.of(context).pop(location);
// Legacy path for plain strings — wrap in structured map.
// Tries to look up a pincode from the database for the given city name.
final match = _locationDb.cast<_LocationItem?>().firstWhere(
(loc) => loc!.city.toLowerCase() == location.toLowerCase() ||
loc.displayTitle.toLowerCase() == location.toLowerCase(),
orElse: () => null,
);
_selectWithPincode(location, pincode: match?.pincode);
}
Future<void> _useCurrentLocation() async {
@@ -122,13 +138,14 @@ class _SearchScreenState extends State<SearchScreen> {
if (permission == LocationPermission.deniedForever || permission == LocationPermission.denied) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Location permission denied')));
Navigator.of(context).pop('Current Location');
Navigator.of(context).pop(<String, dynamic>{'label': 'Current Location', 'pincode': 'all'});
}
return;
}
final pos = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best);
String label = 'Current Location';
try {
final placemarks = await placemarkFromCoordinates(pos.latitude, pos.longitude);
if (placemarks.isNotEmpty) {
@@ -137,17 +154,24 @@ class _SearchScreenState extends State<SearchScreen> {
if ((p.subLocality ?? '').isNotEmpty) parts.add(p.subLocality!);
if ((p.locality ?? '').isNotEmpty) parts.add(p.locality!);
if ((p.subAdministrativeArea ?? '').isNotEmpty) parts.add(p.subAdministrativeArea!);
final label = parts.isNotEmpty ? parts.join(', ') : 'Current Location';
if (mounted) Navigator.of(context).pop(label);
return;
if (parts.isNotEmpty) label = parts.join(', ');
}
} catch (_) {}
if (mounted) Navigator.of(context).pop('Current Location');
if (mounted) {
// Return lat/lng so home_screen can use haversine filtering
Navigator.of(context).pop(<String, dynamic>{
'label': label,
'pincode': 'all',
'lat': pos.latitude,
'lng': pos.longitude,
});
}
return;
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
Navigator.of(context).pop('Current Location');
Navigator.of(context).pop(<String, dynamic>{'label': 'Current Location', 'pincode': 'all'});
}
} finally {
if (mounted) setState(() => _loadingLocation = false);
@@ -320,7 +344,7 @@ class _SearchScreenState extends State<SearchScreen> {
subtitle: loc.pincode != null && loc.pincode!.isNotEmpty
? Text(loc.pincode!, style: TextStyle(color: Colors.grey[500], fontSize: 13))
: null,
onTap: () => _selectAndClose(loc.returnValue),
onTap: () => _selectWithPincode(loc.displayTitle, pincode: loc.pincode),
);
},
),