import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import 'package:travel_mate/components/widgets/user_state_widget.dart'; import '../home/show_trip_details_content.dart'; import 'package:travel_mate/components/home/trip_card.dart'; import 'package:travel_mate/services/trip_image_service.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; /// Service pour charger les images manquantes final TripImageService _tripImageService = TripImageService(); @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 UserStateWrapper( builder: (context, 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: 16), 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) { // Charger les images manquantes en arrière-plan _loadMissingImagesInBackground(trips); return Column( children: trips .map( (trip) => TripCard(trip: trip, onTap: () => _showTripDetails(trip)), ) .toList(), ); } /// Charge les images manquantes en arrière-plan void _loadMissingImagesInBackground(List trips) { // Lancer le chargement des images en arrière-plan sans bloquer l'UI WidgetsBinding.instance.addPostFrameCallback((_) { _tripImageService.loadMissingImages(trips); }); } /// Navigate to trip details page Future _showTripDetails(Trip trip) 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), ); } } } }