feat: city selection now uses haversine radius filtering (10km)
Enrich kerala_pincodes.json with lat/lng for all 463 entries via
pgeocode offline DB (453 exact matches + 10 district centroids).
Update SearchScreen _LocationItem to carry lat/lng fields, load them
from JSON on init, and pass them through every selection path
(_selectWithPincode, _selectAndClose, search result onTap).
Result: selecting Chavakkad (or any Kerala city) now pops
{label, pincode, lat:10.59322, lng:76.0297} → home_screen saves coords
to prefs → getEventsByLocation sends lat/lng to Django → haversine
filtering returns events within 10km radius, expanding to 25/50/100km
if fewer than 6 events found.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,15 @@ import '../core/utils/error_utils.dart';
|
|||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:geocoding/geocoding.dart';
|
import 'package:geocoding/geocoding.dart';
|
||||||
|
|
||||||
/// Data model for a location suggestion (city + optional pincode).
|
/// Data model for a location suggestion (city + optional pincode + optional coords).
|
||||||
class _LocationItem {
|
class _LocationItem {
|
||||||
final String city;
|
final String city;
|
||||||
final String? district;
|
final String? district;
|
||||||
final String? pincode;
|
final String? pincode;
|
||||||
|
final double? lat;
|
||||||
|
final double? lng;
|
||||||
|
|
||||||
const _LocationItem({required this.city, this.district, this.pincode});
|
const _LocationItem({required this.city, this.district, this.pincode, this.lat, this.lng});
|
||||||
|
|
||||||
String get displayTitle => district != null && district!.isNotEmpty ? '$city, $district' : city;
|
String get displayTitle => district != null && district!.isNotEmpty ? '$city, $district' : city;
|
||||||
String get displaySubtitle => pincode ?? '';
|
String get displaySubtitle => pincode ?? '';
|
||||||
@@ -71,6 +73,8 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||||||
city: e['city'] as String,
|
city: e['city'] as String,
|
||||||
district: e['district'] as String?,
|
district: e['district'] as String?,
|
||||||
pincode: e['pincode'] as String?,
|
pincode: e['pincode'] as String?,
|
||||||
|
lat: (e['lat'] as num?)?.toDouble(),
|
||||||
|
lng: (e['lng'] as num?)?.toDouble(),
|
||||||
)).toList();
|
)).toList();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -106,24 +110,28 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pop with a structured result so home_screen can update both the display
|
/// Pop with a structured result so home_screen can update the display label,
|
||||||
/// label AND the pincode / GPS coordinates used for API filtering.
|
/// pincode, and GPS coordinates used for haversine filtering.
|
||||||
void _selectWithPincode(String label, {String? pincode}) {
|
void _selectWithPincode(String label, {String? pincode, double? lat, double? lng}) {
|
||||||
Navigator.of(context).pop(<String, dynamic>{
|
final result = <String, dynamic>{
|
||||||
'label': label,
|
'label': label,
|
||||||
'pincode': pincode ?? 'all',
|
'pincode': pincode ?? 'all',
|
||||||
});
|
};
|
||||||
|
if (lat != null && lng != null) {
|
||||||
|
result['lat'] = lat;
|
||||||
|
result['lng'] = lng;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectAndClose(String location) {
|
void _selectAndClose(String location) {
|
||||||
// Legacy path for plain strings — wrap in structured map.
|
// Looks up pincode + coordinates from the database for the given city name.
|
||||||
// Tries to look up a pincode from the database for the given city name.
|
|
||||||
final match = _locationDb.cast<_LocationItem?>().firstWhere(
|
final match = _locationDb.cast<_LocationItem?>().firstWhere(
|
||||||
(loc) => loc!.city.toLowerCase() == location.toLowerCase() ||
|
(loc) => loc!.city.toLowerCase() == location.toLowerCase() ||
|
||||||
loc.displayTitle.toLowerCase() == location.toLowerCase(),
|
loc.displayTitle.toLowerCase() == location.toLowerCase(),
|
||||||
orElse: () => null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
_selectWithPincode(location, pincode: match?.pincode);
|
_selectWithPincode(location, pincode: match?.pincode, lat: match?.lat, lng: match?.lng);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _useCurrentLocation() async {
|
Future<void> _useCurrentLocation() async {
|
||||||
@@ -344,7 +352,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||||||
subtitle: loc.pincode != null && loc.pincode!.isNotEmpty
|
subtitle: loc.pincode != null && loc.pincode!.isNotEmpty
|
||||||
? Text(loc.pincode!, style: TextStyle(color: Colors.grey[500], fontSize: 13))
|
? Text(loc.pincode!, style: TextStyle(color: Colors.grey[500], fontSize: 13))
|
||||||
: null,
|
: null,
|
||||||
onTap: () => _selectWithPincode(loc.displayTitle, pincode: loc.pincode),
|
onTap: () => _selectWithPincode(loc.displayTitle, pincode: loc.pincode, lat: loc.lat, lng: loc.lng),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user