import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; import '../../../core/constants/design_system.dart'; import '../../../core/constants/env_config.dart'; import '../../../core/theme/theme_provider.dart'; import '../../../core/utils/page_transitions.dart'; import '../../../data/datasources/event_remote_data_source.dart'; import '../../state_management/event_bloc.dart'; import '../../widgets/animated_widgets.dart'; import '../../widgets/custom_button.dart'; import '../../widgets/custom_snackbar.dart'; import '../../widgets/modern_empty_state.dart'; import '../../widgets/shimmer_loading.dart'; import '../dialogs/add_event_dialog.dart'; import 'event_card.dart'; /// Écran principal des événements avec design moderne et compact. /// /// Cet écran affiche une liste d'événements avec gestion d'états améliorée, /// animations fluides, et interface utilisateur optimisée. /// /// **Fonctionnalités:** /// - Affichage de la liste des événements /// - Création d'événements /// - Actions sur les événements (réaction, participation, etc.) /// - Gestion des états (chargement, erreur, vide) /// - Pull-to-refresh class EventScreen extends StatefulWidget { const EventScreen({ required this.userId, required this.userFirstName, required this.userLastName, required this.profileImageUrl, super.key, }); final String userId; final String userFirstName; final String userLastName; final String profileImageUrl; @override State createState() => _EventScreenState(); } class _EventScreenState extends State { late final EventRemoteDataSource _eventRemoteDataSource; @override void initState() { super.initState(); _eventRemoteDataSource = EventRemoteDataSource(http.Client()); _loadEvents(); } /// Charge les événements au démarrage. void _loadEvents() { context.read().add(LoadEvents(widget.userId)); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: _buildAppBar(theme), body: BlocBuilder( builder: (context, state) { if (state is EventLoading) { return _buildLoadingState(theme); } else if (state is EventLoaded) { return _buildLoadedState(context, theme, state); } else if (state is EventError) { return _buildErrorState(context, theme, state.message); } return _buildEmptyState(context, theme); }, ), floatingActionButton: _buildFloatingActionButton(context, theme), ); } // ============================================================================ // APP BAR // ============================================================================ /// Construit la barre d'application. PreferredSizeWidget _buildAppBar(ThemeData theme) { return AppBar( elevation: 0, scrolledUnderElevation: 2, title: Text( 'Événements', style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, letterSpacing: -0.5, ), ), actions: [ IconButton( icon: const Icon(Icons.search_rounded, size: 22), tooltip: 'Rechercher', onPressed: _handleSearch, ), IconButton( icon: const Icon(Icons.add_circle_outline_rounded, size: 22), tooltip: 'Créer un événement', onPressed: _navigateToCreateEvent, ), ], ); } // ============================================================================ // ÉTATS // ============================================================================ /// Construit l'état de chargement avec skeleton loaders. Widget _buildLoadingState(ThemeData theme) { return SkeletonList( itemCount: 3, skeletonWidget: const EventCardSkeleton(), ); } /// Construit l'état avec événements chargés. Widget _buildLoadedState( BuildContext context, ThemeData theme, EventLoaded state, ) { final events = state.events; if (events.isEmpty) { return _buildEmptyState(context, theme); } return RefreshIndicator( onRefresh: _handleRefresh, color: theme.colorScheme.primary, child: ListView.separated( padding: DesignSystem.paddingAll(DesignSystem.spacingLg), physics: const AlwaysScrollableScrollPhysics(), itemCount: events.length, separatorBuilder: (context, index) => const SizedBox(height: DesignSystem.spacingMd), itemBuilder: (context, index) { final event = events[index]; return FadeInWidget( delay: Duration(milliseconds: index * 50), child: EventCard( key: ValueKey(event.id), event: event, userId: widget.userId, userFirstName: widget.userFirstName, userLastName: widget.userLastName, profileImageUrl: widget.profileImageUrl, onReact: () => _handleReact(event.id), onComment: () => _handleComment(event.id), onShare: () => _handleShare(event.id), onParticipate: () => _handleParticipate(event.id), onCloseEvent: () => _handleCloseEvent(event.id), onReopenEvent: () => _handleReopenEvent(event.id), onRemoveEvent: (String eventId) => _handleRemoveEvent(eventId), status: event.status, ), ); }, ), ); } /// Construit l'état vide. Widget _buildEmptyState(BuildContext context, ThemeData theme) { return RefreshIndicator( onRefresh: _handleRefresh, color: theme.colorScheme.primary, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: SizedBox( height: MediaQuery.of(context).size.height - 200, child: ModernEmptyState( illustration: EmptyStateIllustration.events, title: 'Aucun événement disponible', description: 'Créez votre premier événement et commencez à organiser des moments inoubliables avec vos amis', actionLabel: 'Créer un événement', onAction: _navigateToCreateEvent, ), ), ), ); } /// Construit l'état d'erreur. Widget _buildErrorState( BuildContext context, ThemeData theme, String message, ) { return RefreshIndicator( onRefresh: _handleRefresh, color: theme.colorScheme.primary, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: SizedBox( height: MediaQuery.of(context).size.height - 200, child: Center( child: Padding( padding: DesignSystem.paddingAll(DesignSystem.spacingXl), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline_rounded, size: 56, color: theme.colorScheme.error.withOpacity(0.7), ), const SizedBox(height: DesignSystem.spacingLg), Text( 'Erreur', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, fontSize: 17, ), textAlign: TextAlign.center, ), const SizedBox(height: DesignSystem.spacingSm), Text( message, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), fontSize: 14, ), textAlign: TextAlign.center, ), const SizedBox(height: DesignSystem.spacingXl), CustomButton( text: 'Réessayer', icon: Icons.refresh_rounded, onPressed: _loadEvents, variant: ButtonVariant.outlined, size: ButtonSize.medium, ), ], ), ), ), ), ), ); } // ============================================================================ // FLOATING ACTION BUTTON // ============================================================================ /// Construit le bouton flottant (compact). Widget _buildFloatingActionButton(BuildContext context, ThemeData theme) { return FloatingActionButton( onPressed: _navigateToCreateEvent, tooltip: 'Créer un événement', elevation: 2, child: const Icon(Icons.add_rounded, size: 26), ); } // ============================================================================ // ACTIONS // ============================================================================ /// Gère la recherche. void _handleSearch() { context.showInfo('Recherche à venir'); } /// Navigue vers la création d'événement avec animation. void _navigateToCreateEvent() { context.pushSlideUp( AddEventPage( userId: widget.userId, userFirstName: widget.userFirstName, userLastName: widget.userLastName, ), ); } /// Gère le rafraîchissement. Future _handleRefresh() async { context.read().add(LoadEvents(widget.userId)); await Future.delayed(const Duration(milliseconds: 500)); } /// Gère la réaction à un événement. Future _handleReact(String eventId) async { try { await _eventRemoteDataSource.reactToEvent(eventId, widget.userId); if (mounted) { context.showSuccess('Réaction enregistrée'); // Recharger les événements pour mettre à jour les compteurs context.read().add(LoadEvents(widget.userId)); } } catch (e) { if (mounted) { context.showError('Erreur lors de la réaction: ${e.toString()}'); } if (EnvConfig.enableDetailedLogs) { debugPrint('[EventScreen] Erreur réaction: $e'); } } } /// Gère le commentaire sur un événement. void _handleComment(String eventId) { context.showInfo('Fonctionnalité de commentaires à venir'); } /// Gère le partage d'un événement. void _handleShare(String eventId) { context.showInfo('Fonctionnalité de partage à venir'); } /// Gère la participation à un événement. Future _handleParticipate(String eventId) async { try { await _eventRemoteDataSource.participateInEvent(eventId, widget.userId); if (mounted) { context.showSuccess('Participation enregistrée'); // Recharger les événements pour mettre à jour les participants context.read().add(LoadEvents(widget.userId)); } } catch (e) { if (mounted) { context.showError('Erreur lors de la participation: ${e.toString()}'); } if (EnvConfig.enableDetailedLogs) { debugPrint('[EventScreen] Erreur participation: $e'); } } } /// Gère la fermeture d'un événement. void _handleCloseEvent(String eventId) { context.read().add(CloseEvent(eventId)); context.showSuccess('L\'événement a été fermé avec succès'); } /// Gère la réouverture d'un événement. void _handleReopenEvent(String eventId) { context.read().add(ReopenEvent(eventId)); context.showSuccess('L\'événement a été rouvert avec succès'); } /// Gère la suppression d'un événement. void _handleRemoveEvent(String eventId) { context.read().add(RemoveEvent(eventId)); } }