Feature: Event Discovery Screen — Live Data
Branch: feat/event-discovery-live
Date: 2026-03-18
Impact: Core screen rewritten from static mock data to fully live Supabase integration
What Changed
EventDiscoveryScreen was the most-visited screen in the app but ran entirely on hardcoded mock data. This feature replaces every placeholder with real data and navigation.
Before vs. After
| Area | Before | After |
|---|---|---|
| Widget type | StatefulWidget | ConsumerStatefulWidget |
| Events | 3 hardcoded mock maps | eventNotifierProvider (Supabase RPC) |
| Notification badge | Hardcoded "3" | unreadNotificationsCountProvider (hidden when 0, capped at 99+) |
| Filters | Visual only, no effect | Client-side filter on real event list |
| Tapping a card | No-op | Navigates to /home/event-detail/:eventId |
| RSVP button | Shows snackbar only | joinEvent() / leaveEvent() → optimistic update |
| Share button | No-op | ShareService.shareEvent() |
| Create Event FAB | No-op | Navigates to /home/create-event |
| Notifications icon | No-op | Navigates to /home/notifications |
| Search | Static suggestion list | searchEvents() RPC, results via eventSearchResultsProvider |
| Empty / error states | None | Loading spinner, error with retry, empty state |
| Pagination | None | Load-more trigger at list bottom + pull-to-refresh |
Architecture
State
The screen watches two providers:
final eventState = ref.watch(eventNotifierProvider); // events, loading, error
final unreadCount = ref.watch(unreadNotificationsCountProvider); // badgeFiltering
Filters run client-side on the already-fetched list. The getEventsWithFriends RPC pre-loads social proof data (friends going count), so local filtering is instant and avoids extra network round-trips.
List<Event> _applyFilters(List<Event> events) {
// filter by: today, weekend, free, friends, category
}Pagination
The SliverChildBuilderDelegate uses childCount: events.length + 1. The extra item at index events.length triggers loadMoreEvents() when scrolled into view and shows a spinner while loading.
Search
EventSearchDelegate now accepts a WidgetRef and wraps its results in a Consumer widget — the correct pattern for using Riverpod providers inside SearchDelegate, which is not a ConsumerWidget.
Files Changed
| File | Change |
|---|---|
lib/features/events/screens/event_discovery_screen.dart | Full rewrite |
Known Limitations
- Category filter matches on
event.category.name(enum name, lowercase). If events use display labels that differ from enum names, filtering may miss results — a dedicatedcategoryquery param would be cleaner long-term. isFeaturedis currently set totrueonly for the first card when no filter is active. A properisFeaturedflag should come from the DB.