import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import '../home/show_trip_details_content.dart'; import '../../blocs/user/user_bloc.dart'; import '../../blocs/user/user_state.dart'; import '../../blocs/trip/trip_bloc.dart'; import '../../blocs/trip/trip_state.dart'; import '../../blocs/trip/trip_event.dart'; import '../../models/trip.dart'; /// Home content widget for the main application dashboard. /// /// This widget serves as the primary content area of the home screen, /// displaying user trips and providing navigation to trip management /// features. Key functionality includes: /// - Loading and displaying user trips /// - Creating new trips /// - Viewing trip details /// - Managing trip state with proper user authentication /// /// The widget maintains state persistence using AutomaticKeepAliveClientMixin /// to preserve content when switching between tabs. class HomeContent extends StatefulWidget { /// Creates a home content widget. const HomeContent({super.key}); @override State createState() => _HomeContentState(); } class _HomeContentState extends State with AutomaticKeepAliveClientMixin { /// Preserves widget state when switching between tabs @override bool get wantKeepAlive => true; /// Flag to prevent duplicate trip loading operations bool _hasLoadedTrips = false; @override void initState() { super.initState(); // Use addPostFrameCallback to wait for the widget tree to be ready WidgetsBinding.instance.addPostFrameCallback((_) { _loadTripsIfUserLoaded(); }); } /// Loads trips if a user is currently loaded and trips haven't been loaded yet. /// /// Checks the current user state and initiates trip loading if the user is /// authenticated and trips haven't been loaded previously. This prevents /// duplicate loading operations. void _loadTripsIfUserLoaded() { if (!_hasLoadedTrips && mounted) { final userState = context.read().state; if (userState is UserLoaded) { _hasLoadedTrips = true; context.read().add( LoadTripsByUserId(userId: userState.user.id), ); } } } @override Widget build(BuildContext context) { super.build(context); // Important pour AutomaticKeepAliveClientMixin return BlocBuilder( builder: (context, userState) { if (userState is UserLoading) { return Scaffold(body: Center(child: CircularProgressIndicator())); } if (userState is UserError) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error, size: 64, color: Colors.red), SizedBox(height: 16), Text('Erreur: ${userState.message}'), ], ), ), ); } if (userState is! UserLoaded) { return Scaffold(body: Center(child: Text('Veuillez vous connecter'))); } final user = userState.user; return BlocConsumer( listener: (context, tripState) { if (tripState is TripOperationSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(tripState.message), backgroundColor: Colors.green, ), ); } else if (tripState is TripError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(tripState.message), backgroundColor: Colors.red, ), ); } else if (tripState is TripCreated) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Voyage en cours de création...'), backgroundColor: Colors.blue, duration: Duration(seconds: 1), ), ); } }, builder: (context, tripState) { // AJOUTÉ : Si l'état est initial et qu'on n'a pas encore chargé, charger maintenant if (tripState is TripInitial && !_hasLoadedTrips) { WidgetsBinding.instance.addPostFrameCallback((_) { _loadTripsIfUserLoaded(); }); } return Scaffold( body: RefreshIndicator( onRefresh: () async { context.read().add( LoadTripsByUserId(userId: user.id), ); await Future.delayed(Duration(milliseconds: 500)); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Bonjour ${user.prenom} !', style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black, ), ), const SizedBox(height: 8), Text( 'Vos voyages', style: TextStyle( fontSize: 16, color: Theme.of(context).brightness == Brightness.dark ? Colors.white70 : Colors.grey[600], ), ), const SizedBox(height: 20), if (tripState is TripLoading || tripState is TripCreated) _buildLoadingState() else if (tripState is TripError) _buildErrorState(tripState.message, user.id) else if (tripState is TripLoaded) tripState.trips.isEmpty ? _buildEmptyState() : _buildTripsList(tripState.trips) else if (tripState is TripInitial) _buildLoadingState() // Afficher le loader pendant le premier chargement else _buildEmptyState(), const SizedBox(height: 80), ], ), ), ), floatingActionButton: FloatingActionButton( onPressed: () async { final tripBloc = context.read(); final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => const CreateTripContent(), ), ); if (result == true && mounted) { tripBloc.add(LoadTripsByUserId(userId: user.id)); } }, backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, child: const Icon(Icons.add), ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); }, ); }, ); } Widget _buildLoadingState() { return Center( child: Padding( padding: EdgeInsets.all(32), child: CircularProgressIndicator(), ), ); } Widget _buildErrorState(String error, String userId) { return Center( child: Padding( padding: EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error, size: 64, color: Colors.red), SizedBox(height: 16), Text('Erreur lors du chargement des voyages'), SizedBox(height: 8), Text( error, textAlign: TextAlign.center, style: TextStyle(fontSize: 12, color: Colors.grey), ), SizedBox(height: 16), ElevatedButton( onPressed: () { context.read().add(LoadTripsByUserId(userId: userId)); }, child: Text('Réessayer'), ), ], ), ), ); } Widget _buildEmptyState() { return Center( child: Padding( padding: EdgeInsets.all(32), child: Column( children: [ Icon(Icons.travel_explore, size: 80, color: Colors.grey[400]), SizedBox(height: 16), Text( 'Aucun voyage', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( 'Créez votre premier voyage en appuyant sur le bouton +', textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), ], ), ), ); } Widget _buildTripsList(List trips) { return Column(children: trips.map((trip) => _buildTripCard(trip)).toList()); } Widget _buildTripCard(Trip trip) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; final textColor = isDarkMode ? Colors.white : Colors.black; final subtextColor = isDarkMode ? Colors.white70 : Colors.grey[600]; return Card( margin: EdgeInsets.only(bottom: 12), elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image en haut de la carte Container( height: 200, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), color: Colors.grey[300], ), child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), child: trip.imageUrl != null && trip.imageUrl!.isNotEmpty ? Image.network( trip.imageUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Container( color: Colors.grey[300], child: Icon( Icons.image_not_supported, size: 50, color: Colors.grey[600], ), ); }, ) : Container( color: Colors.grey[300], child: Icon( Icons.travel_explore, size: 50, color: Colors.grey[600], ), ), ), ), // Contenu de la carte Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Nom du voyage Text( trip.title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: textColor, ), ), SizedBox(height: 8), // Section dates, participants et bouton Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Colonne gauche : dates et participants Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Dates Row( children: [ Icon( Icons.calendar_today, size: 16, color: subtextColor, ), SizedBox(width: 4), Text( '${_formatDate(trip.startDate)} - ${_formatDate(trip.endDate)}', style: TextStyle( fontSize: 14, color: subtextColor, ), ), ], ), SizedBox(height: 8), // Nombre de participants Row( children: [ Icon(Icons.people, size: 16, color: subtextColor), SizedBox(width: 4), Text( '${trip.participants.length} participant${trip.participants.length > 1 ? 's' : ''}', style: TextStyle( fontSize: 14, color: subtextColor, ), ), ], ), ], ), ), // Bouton "Voir" à droite ElevatedButton( onPressed: () async { final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => ShowTripDetailsContent(trip: trip), ), ); if (result == true && mounted) { final userState = context.read().state; if (userState is UserLoaded) { context.read().add( LoadTripsByUserId(userId: userState.user.id), ); } } }, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Theme.of( context, ).colorScheme.onPrimary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), ), child: Text('Voir', style: TextStyle(fontSize: 14)), ), ], ), ], ), ), ], ), ); } // Color _getStatusColor(Trip trip) { // final now = DateTime.now(); // if (now.isBefore(trip.startDate)) { // return Colors.blue; // } else if (now.isAfter(trip.endDate)) { // return Colors.grey; // } else { // return Colors.green; // } // } // String _getStatusText(Trip trip) { // final now = DateTime.now(); // if (now.isBefore(trip.startDate)) { // return 'À venir'; // } else if (now.isAfter(trip.endDate)) { // return 'Terminé'; // } else { // return 'En cours'; // } // } String _formatDate(DateTime date) { return '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}'; } }