perf: optimize loading time — paginated API, slim payloads, local category filtering

Backend: Rewrote EventListAPI to query per-type with DB-level LIMIT
instead of loading all 734 events into memory. Added slim serializer
(32KB vs 154KB). Added DB indexes on event_type_id and pincode.

Frontend: Category chips now filter locally from _allEvents (instant,
no API call). Top Events and category sections always show all types
regardless of selected category. Added TTL caching for event types
(30min) and events (5min). Reduced API timeout from 30s to 10s.
Added memCacheHeight to all CachedNetworkImage widgets. Batched
setState calls from 5 to 2 during startup. Cached _eventDates getter.

Switched baseUrl to em.eventifyplus.com (Django via Nginx+SSL).
Added initialEvent param to LearnMoreScreen for instant detail views.
Resolved relative media URLs for category icons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 10:05:23 +05:30
parent 87cc56dc64
commit b55f02e057
8 changed files with 357 additions and 82 deletions

View File

@@ -5,9 +5,9 @@ import 'package:http/http.dart' as http;
import '../storage/token_storage.dart';
class ApiClient {
static const Duration _timeout = Duration(seconds: 30);
static const Duration _timeout = Duration(seconds: 10);
// Set to true to enable mock/offline development mode (useful when backend is unavailable)
static const bool _developmentMode = true;
static const bool _developmentMode = false;
/// POST request
///
@@ -57,6 +57,39 @@ class ApiClient {
'email': email,
'phone_number': finalBody['phone_number'] ?? '+1234567890',
};
} else if (url.contains('/events/type-list/')) {
if (kDebugMode) debugPrint('Development mode: returning mock event types');
return {
'event_types': [
{'id': 1, 'event_type': 'Concert', 'event_type_icon': null},
{'id': 2, 'event_type': 'Workshop', 'event_type_icon': null},
{'id': 3, 'event_type': 'Festival', 'event_type_icon': null},
{'id': 4, 'event_type': 'Sports', 'event_type_icon': null},
{'id': 5, 'event_type': 'Conference', 'event_type_icon': null},
{'id': 6, 'event_type': 'Exhibition', 'event_type_icon': null},
],
};
} else if (url.contains('/events/pincode-events/')) {
if (kDebugMode) debugPrint('Development mode: returning mock events');
return {'events': _mockEvents};
} else if (url.contains('/events/event-details/')) {
if (kDebugMode) debugPrint('Development mode: returning mock event detail');
final eventId = finalBody['event_id'] ?? 1;
final match = _mockEvents.where((e) => e['id'] == eventId);
return match.isNotEmpty
? Map<String, dynamic>.from(match.first)
: Map<String, dynamic>.from(_mockEvents.first);
} else if (url.contains('/events/events-by-month-year/')) {
if (kDebugMode) debugPrint('Development mode: returning mock calendar');
return {
'total_number_of_events': 3,
'dates': ['2026-04-05', '2026-04-12', '2026-04-20'],
'date_events': [
{'date': '2026-04-05', 'count': 1},
{'date': '2026-04-12', 'count': 2},
{'date': '2026-04-20', 'count': 1},
],
};
}
}
@@ -103,6 +136,153 @@ class ApiClient {
return _handleResponse(url, response, finalParams);
}
// ---------------------------------------------------------------------------
// Mock event data for development / offline mode
// ---------------------------------------------------------------------------
static final List<Map<String, dynamic>> _mockEvents = [
{
'id': 1,
'name': 'Tech Innovation Summit 2026',
'title': 'Tech Innovation Summit',
'description':
'Join industry leaders for a two-day summit exploring the latest breakthroughs in AI, cloud computing, and sustainable technology. Featuring keynote speakers, hands-on workshops, and networking sessions.',
'start_date': '2026-04-15',
'end_date': '2026-04-16',
'start_time': '09:00',
'end_time': '18:00',
'pincode': '560001',
'place': 'Bengaluru International Exhibition Centre',
'is_bookable': true,
'event_type': 5,
'thumb_img': 'https://picsum.photos/seed/event1/600/400',
'images': [
{'is_primary': true, 'image': 'https://picsum.photos/seed/event1a/800/500'},
{'is_primary': false, 'image': 'https://picsum.photos/seed/event1b/800/500'},
],
'important_information': 'Please carry a valid photo ID for entry.',
'venue_name': 'BIEC Hall 2',
'event_status': 'active',
'latitude': 13.0147,
'longitude': 77.5636,
'location_name': 'Bengaluru',
'important_info': [
{'title': 'Entry', 'value': 'Free with registration'},
{'title': 'Parking', 'value': 'Available on-site'},
],
},
{
'id': 2,
'name': 'Sunset Music Festival',
'title': 'Sunset Music Festival',
'description':
'An open-air music festival featuring live performances from top artists across genres. Enjoy food stalls, art installations, and an unforgettable sunset experience.',
'start_date': '2026-04-20',
'end_date': '2026-04-20',
'start_time': '16:00',
'end_time': '23:00',
'pincode': '400001',
'place': 'Marine Drive Amphitheatre',
'is_bookable': true,
'event_type': 1,
'thumb_img': 'https://picsum.photos/seed/event2/600/400',
'images': [
{'is_primary': true, 'image': 'https://picsum.photos/seed/event2a/800/500'},
],
'venue_name': 'Marine Drive Amphitheatre',
'event_status': 'active',
'latitude': 18.9432,
'longitude': 72.8235,
'location_name': 'Mumbai',
'important_info': [
{'title': 'Age Limit', 'value': '16+'},
],
},
{
'id': 3,
'name': 'Creative Design Workshop',
'title': 'Hands-on Design Workshop',
'description':
'A full-day workshop on UI/UX design principles, prototyping in Figma, and building design systems. Perfect for beginners and intermediate designers.',
'start_date': '2026-05-03',
'end_date': '2026-05-03',
'start_time': '10:00',
'end_time': '17:00',
'pincode': '110001',
'place': 'Design Hub Co-working',
'is_bookable': true,
'event_type': 2,
'thumb_img': 'https://picsum.photos/seed/event3/600/400',
'images': [
{'is_primary': true, 'image': 'https://picsum.photos/seed/event3a/800/500'},
],
'venue_name': 'Design Hub',
'event_status': 'active',
'latitude': 28.6139,
'longitude': 77.2090,
'location_name': 'New Delhi',
'important_info': [
{'title': 'Bring', 'value': 'Laptop with Figma installed'},
{'title': 'Seats', 'value': '30 max'},
],
},
{
'id': 4,
'name': 'Marathon for a Cause',
'title': 'City Marathon 2026',
'description':
'Run for fitness, run for charity! Choose from 5K, 10K, or full marathon routes through the city. All proceeds support local education initiatives.',
'start_date': '2026-04-12',
'end_date': '2026-04-12',
'start_time': '05:30',
'end_time': '12:00',
'pincode': '600001',
'place': 'Marina Beach Road',
'is_bookable': true,
'event_type': 4,
'thumb_img': 'https://picsum.photos/seed/event4/600/400',
'images': [
{'is_primary': true, 'image': 'https://picsum.photos/seed/event4a/800/500'},
],
'venue_name': 'Marina Beach',
'event_status': 'active',
'latitude': 13.0500,
'longitude': 80.2824,
'location_name': 'Chennai',
'important_info': [
{'title': 'Registration', 'value': 'Closes April 10'},
],
},
{
'id': 5,
'name': 'Art & Culture Exhibition',
'title': 'Contemporary Art Exhibition',
'description':
'Explore contemporary artworks from emerging and established artists. The exhibition features paintings, sculptures, and digital art installations.',
'start_date': '2026-05-10',
'end_date': '2026-05-15',
'start_time': '11:00',
'end_time': '20:00',
'pincode': '500001',
'place': 'Salar Jung Museum Grounds',
'is_bookable': true,
'event_type': 6,
'thumb_img': 'https://picsum.photos/seed/event5/600/400',
'images': [
{'is_primary': true, 'image': 'https://picsum.photos/seed/event5a/800/500'},
{'is_primary': false, 'image': 'https://picsum.photos/seed/event5b/800/500'},
],
'venue_name': 'Salar Jung Museum',
'event_status': 'active',
'latitude': 17.3713,
'longitude': 78.4804,
'location_name': 'Hyderabad',
'important_info': [
{'title': 'Entry Fee', 'value': '₹200'},
{'title': 'Photography', 'value': 'Allowed without flash'},
],
},
];
/// Build request body and attach token + username if available
Future<Map<String, dynamic>> _buildAuthBody(Map<String, dynamic>? body, bool requiresAuth) async {
final Map<String, dynamic> finalBody = {};

View File

@@ -3,7 +3,11 @@ class ApiEndpoints {
// Change this to your desired backend base URL (local or UAT)
// For local Django dev use: "http://127.0.0.1:8000/api"
// For UAT: "https://uat.eventifyplus.com/api"
static const String baseUrl = "https://uat.eventifyplus.com/api";
static const String baseUrl = "https://em.eventifyplus.com/api";
/// Base URL for media files (images, icons uploaded via Django admin).
/// Relative paths like `/media/...` are resolved against this.
static const String mediaBaseUrl = "https://em.eventifyplus.com";
// Auth
static const String register = "$baseUrl/user/register/";