feat: HOME-007 — server-side event title/description search (q param)

This commit is contained in:
2026-04-04 17:33:56 +05:30
parent 632754415d
commit 64e7323213
2 changed files with 25 additions and 4 deletions

View File

@@ -43,10 +43,11 @@ class EventsService {
/// Get events filtered by pincode with pagination.
/// [page] starts at 1. [pageSize] defaults to 50.
/// Returns a list of events for the requested page.
Future<List<EventModel>> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5}) async {
// Use cache for 'all' pincode queries (first page only for initial load)
Future<List<EventModel>> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5, String q = ''}) async {
// Use cache for 'all' pincode queries (first page only, no active search)
if (pincode == 'all' &&
page == 1 &&
q.isEmpty &&
_cachedAllEvents != null &&
_eventsCacheTime != null &&
DateTime.now().difference(_eventsCacheTime!) < _eventsCacheTTL) {
@@ -56,6 +57,8 @@ class EventsService {
final Map<String, dynamic> body = {'pincode': pincode, 'page': page, 'page_size': pageSize};
// Diverse mode: fetch a few events per type so all categories are represented
if (perType > 0 && page == 1) body['per_type'] = perType;
// Server-side search filter
if (q.isNotEmpty) body['q'] = q;
final res = await _api.post(
ApiEndpoints.eventsByPincode,

View File

@@ -302,7 +302,9 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
builder: (context, scrollController) {
String query = '';
List<EventModel> results = List.from(_events);
bool searching = false;
return StatefulBuilder(builder: (context, setModalState) {
// Instant client-side filter while typing
void _onQueryChanged(String v) {
query = v.trim().toLowerCase();
final r = _events.where((e) {
@@ -314,6 +316,22 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
});
}
// Server-side search on submit (keyboard action / enter)
Future<void> _onSubmitted(String v) async {
final q = v.trim();
if (q.isEmpty) return;
setModalState(() => searching = true);
try {
final serverResults = await _eventsService.getEventsByPincode(_pincode, q: q);
setModalState(() {
results = serverResults;
searching = false;
});
} catch (_) {
setModalState(() => searching = false);
}
}
return Container(
decoration: BoxDecoration(
color: theme.cardColor,
@@ -357,7 +375,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
autofocus: true,
onChanged: _onQueryChanged,
textInputAction: TextInputAction.search,
onSubmitted: (v) => _onQueryChanged(v),
onSubmitted: _onSubmitted,
),
)
],
@@ -372,7 +390,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
],
),
const SizedBox(height: 12),
if (_loading)
if (_loading || searching)
Center(child: CircularProgressIndicator(color: theme.colorScheme.primary))
else if (results.isEmpty)
Padding(