feat: Phase 2 — 11 high-priority gaps implemented across home, auth, gamification, profile, and event detail
Phase 2 gaps completed: - HOME-001: Hero slider pause-on-touch (GestureDetector wraps PageView) - HOME-003: Calendar bottom sheet with TableCalendar (replaces custom dialog) - AUTH-004: District dropdown on signup (14 Kerala districts) - EVT-003: Mobile sticky "Book Now" bar + desktop CTA wired to CheckoutScreen - ACH-001: Real achievements from dashboard API with fallback defaults - GAM-002: 3-card EP row (Lifetime EP / Liquid EP / Reward Points) - GAM-005: Horizontal tier roadmap Bronze→Silver→Gold→Platinum→Diamond - CTR-001: Submission status chips (PENDING/APPROVED/REJECTED) - CTR-002: +EP badge on approved submissions - PROF-003: Gamification cards on profile screen with Consumer<GamificationProvider> - UX-001: Shimmer skeleton loaders (shimmer ^3.0.0) replacing CircularProgressIndicator Already complete (verified, no changes needed): - HOME-002: Category shelves already built in _buildTypeSection() - LDR-002: Podium visualization already built (_buildPodium / _buildDesktopPodium) - BOOK-004: UPI handled natively by Razorpay SDK Deferred: LOC-001/002 (needs Django haversine endpoint) Skipped: AUTH-002 (OTP needs SMS provider decision) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import '../features/events/services/events_service.dart';
|
||||
import '../features/events/models/event_models.dart';
|
||||
import '../features/gamification/providers/gamification_provider.dart';
|
||||
import '../features/gamification/models/gamification_models.dart';
|
||||
import '../widgets/skeleton_loader.dart';
|
||||
import 'learn_more_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
import '../core/app_decoration.dart';
|
||||
@@ -73,6 +77,11 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
|
||||
_loadProfile();
|
||||
_startAnimations();
|
||||
|
||||
// Load gamification data for profile EP cards
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) context.read<GamificationProvider>().loadAll();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1588,6 +1597,9 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
),
|
||||
),
|
||||
|
||||
// PROF-003: Gamification stat cards
|
||||
SliverToBoxAdapter(child: _buildGamificationCards(theme)),
|
||||
|
||||
// ── Ongoing Events ──
|
||||
if (_ongoingEvents.isNotEmpty) ...[
|
||||
SliverToBoxAdapter(child: sectionTitle('Ongoing Events')),
|
||||
@@ -1657,4 +1669,54 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// PROF-003: Gamification 3-card row (Lifetime EP / Liquid EP / RP)
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
Widget _buildGamificationCards(ThemeData theme) {
|
||||
return Consumer<GamificationProvider>(
|
||||
builder: (context, gp, _) {
|
||||
if (gp.isLoading && gp.profile == null) {
|
||||
return const ProfileStatsSkeleton();
|
||||
}
|
||||
if (gp.profile == null) return const SizedBox.shrink();
|
||||
|
||||
final p = gp.profile!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(18, 8, 18, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
_gamStatCard('Lifetime EP', '${p.lifetimeEp}', Icons.star, const Color(0xFFF59E0B), theme),
|
||||
const SizedBox(width: 10),
|
||||
_gamStatCard('Liquid EP', '${p.currentEp}', Icons.bolt, const Color(0xFF3B82F6), theme),
|
||||
const SizedBox(width: 10),
|
||||
_gamStatCard('Reward Pts', '${p.currentRp}', Icons.redeem, const Color(0xFF10B981), theme),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _gamStatCard(String label, String value, IconData icon, Color color, ThemeData theme) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: color.withOpacity(0.2)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(icon, color: color, size: 22),
|
||||
const SizedBox(height: 4),
|
||||
Text(value, style: TextStyle(fontWeight: FontWeight.w800, fontSize: 16, color: theme.textTheme.bodyLarge?.color)),
|
||||
const SizedBox(height: 2),
|
||||
Text(label, style: TextStyle(color: theme.hintColor, fontSize: 10), textAlign: TextAlign.center),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user