596 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			596 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:em2rp/providers/local_user_provider.dart';
 | |
| import 'package:em2rp/providers/event_provider.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:em2rp/views/widgets/nav/custom_app_bar.dart';
 | |
| import 'package:em2rp/views/widgets/nav/main_drawer.dart';
 | |
| import 'package:provider/provider.dart';
 | |
| import 'package:table_calendar/table_calendar.dart';
 | |
| import 'package:em2rp/models/event_model.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details.dart';
 | |
| import 'package:intl/date_symbol_data_local.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/month_view.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/week_view.dart';
 | |
| import 'package:em2rp/views/event_add_page.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/mobile_calendar_view.dart';
 | |
| import 'package:em2rp/utils/colors.dart';
 | |
| 
 | |
| class CalendarPage extends StatefulWidget {
 | |
|   const CalendarPage({super.key});
 | |
| 
 | |
|   @override
 | |
|   State<CalendarPage> createState() => _CalendarPageState();
 | |
| }
 | |
| 
 | |
| class _CalendarPageState extends State<CalendarPage> {
 | |
|   CalendarFormat _calendarFormat = CalendarFormat.month;
 | |
|   DateTime _focusedDay = DateTime.now();
 | |
|   DateTime? _selectedDay;
 | |
|   EventModel? _selectedEvent;
 | |
|   bool _calendarCollapsed = false;
 | |
|   int _selectedEventIndex = 0;
 | |
| 
 | |
|   @override
 | |
|   void initState() {
 | |
|     super.initState();
 | |
|     initializeDateFormatting('fr_FR', null);
 | |
|     Future.microtask(() => _loadEvents());
 | |
|     // Sélection automatique de l'événement le plus proche de maintenant
 | |
|     WidgetsBinding.instance.addPostFrameCallback((_) {
 | |
|       final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|       final events = eventProvider.events;
 | |
|       if (events.isNotEmpty) {
 | |
|         final now = DateTime.now();
 | |
|         // Pour mobile : sélectionner le premier événement du jour ou le prochain événement à venir
 | |
|         final todayEvents = events
 | |
|             .where((e) =>
 | |
|                 e.startDateTime.year == now.year &&
 | |
|                 e.startDateTime.month == now.month &&
 | |
|                 e.startDateTime.day == now.day)
 | |
|             .toList()
 | |
|           ..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|         EventModel? selected;
 | |
|         DateTime? selectedDay;
 | |
|         int selectedEventIndex = 0;
 | |
|         if (todayEvents.isNotEmpty) {
 | |
|           selected = todayEvents[0];
 | |
|           selectedDay = DateTime(now.year, now.month, now.day);
 | |
|         } else {
 | |
|           // Chercher le prochain événement à venir
 | |
|           final futureEvents = events
 | |
|               .where((e) => e.startDateTime.isAfter(now))
 | |
|               .toList()
 | |
|             ..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|           if (futureEvents.isNotEmpty) {
 | |
|             selected = futureEvents[0];
 | |
|             selectedDay = DateTime(selected.startDateTime.year,
 | |
|                 selected.startDateTime.month, selected.startDateTime.day);
 | |
|           } else {
 | |
|             // Aucun événement à venir, prendre le plus proche dans le passé
 | |
|             events.sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|             selected = events.last;
 | |
|             selectedDay = DateTime(selected.startDateTime.year,
 | |
|                 selected.startDateTime.month, selected.startDateTime.day);
 | |
|           }
 | |
|         }
 | |
|         setState(() {
 | |
|           _selectedDay = selectedDay;
 | |
|           _focusedDay = selectedDay!;
 | |
|           _selectedEventIndex = 0;
 | |
|           _selectedEvent = selected;
 | |
|         });
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   Future<void> _loadEvents() async {
 | |
|     final localAuthProvider =
 | |
|         Provider.of<LocalUserProvider>(context, listen: false);
 | |
|     final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|     final userId = localAuthProvider.uid;
 | |
|     print('Permissions utilisateur: ${localAuthProvider.permissions}');
 | |
|     final canViewAllEvents = localAuthProvider.hasPermission('view_all_events');
 | |
|     print('canViewAllEvents: $canViewAllEvents');
 | |
| 
 | |
|     if (userId != null) {
 | |
|       await eventProvider.loadUserEvents(userId,
 | |
|           canViewAllEvents: canViewAllEvents);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void _changeWeek(int delta) {
 | |
|     setState(() {
 | |
|       _focusedDay = _focusedDay.add(Duration(days: 7 * delta));
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     final eventProvider = Provider.of<EventProvider>(context);
 | |
|     final localUserProvider = Provider.of<LocalUserProvider>(context);
 | |
|     final isAdmin = localUserProvider.hasPermission('view_all_users');
 | |
|     final isMobile = MediaQuery.of(context).size.width < 600;
 | |
| 
 | |
|     if (eventProvider.isLoading) {
 | |
|       return const Scaffold(
 | |
|         body: Center(
 | |
|           child: CircularProgressIndicator(),
 | |
|         ),
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     return Scaffold(
 | |
|       appBar: CustomAppBar(
 | |
|         title: "Calendrier",
 | |
|       ),
 | |
|       drawer: const MainDrawer(currentPage: '/calendar'),
 | |
|       body: isMobile ? _buildMobileLayout() : _buildDesktopLayout(),
 | |
|       floatingActionButton: isAdmin
 | |
|           ? FloatingActionButton(
 | |
|               backgroundColor: Colors.white,
 | |
|               onPressed: () {
 | |
|                 Navigator.of(context).push(
 | |
|                   MaterialPageRoute(
 | |
|                     builder: (context) => const EventAddEditPage(),
 | |
|                   ),
 | |
|                 );
 | |
|               },
 | |
|               tooltip: 'Ajouter un événement',
 | |
|               child: const Icon(Icons.add, color: Colors.red),
 | |
|             )
 | |
|           : null,
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   Widget _buildDesktopLayout() {
 | |
|     final eventProvider = Provider.of<EventProvider>(context);
 | |
|     return Row(
 | |
|       children: [
 | |
|         // Calendrier (65% de la largeur)
 | |
|         Expanded(
 | |
|           flex: 65,
 | |
|           child: _buildCalendar(),
 | |
|         ),
 | |
|         // Détails de l'événement (35% de la largeur)
 | |
|         Expanded(
 | |
|           flex: 35,
 | |
|           child: _selectedEvent != null
 | |
|               ? EventDetails(
 | |
|                   event: _selectedEvent!,
 | |
|                   selectedDate: _selectedDay,
 | |
|                   events: eventProvider.events,
 | |
|                   onSelectEvent: (event, date) {
 | |
|                     setState(() {
 | |
|                       _selectedEvent = event;
 | |
|                       _selectedDay = date;
 | |
|                     });
 | |
|                   },
 | |
|                 )
 | |
|               : Center(
 | |
|                   child: _selectedDay != null
 | |
|                       ? Text('Aucun événement ne démarre à cette date')
 | |
|                       : const Text(
 | |
|                           'Sélectionnez un événement pour voir les détails'),
 | |
|                 ),
 | |
|         ),
 | |
|       ],
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   Widget _buildMobileLayout() {
 | |
|     final eventProvider = Provider.of<EventProvider>(context);
 | |
|     final eventsForSelectedDay = _selectedDay == null
 | |
|         ? []
 | |
|         : eventProvider.events
 | |
|             .where((e) =>
 | |
|                 e.startDateTime.year == _selectedDay!.year &&
 | |
|                 e.startDateTime.month == _selectedDay!.month &&
 | |
|                 e.startDateTime.day == _selectedDay!.day)
 | |
|             .toList()
 | |
|       ..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|     final hasEvents = eventsForSelectedDay.isNotEmpty;
 | |
|     final currentEvent =
 | |
|         hasEvents && _selectedEventIndex < eventsForSelectedDay.length
 | |
|             ? eventsForSelectedDay[_selectedEventIndex]
 | |
|             : null;
 | |
| 
 | |
|     // GESTURE DETECTOR pour swipe vertical (plier/déplier) et horizontal (mois)
 | |
|     return GestureDetector(
 | |
|       onVerticalDragEnd: (details) {
 | |
|         if (details.primaryVelocity != null) {
 | |
|           if (details.primaryVelocity! < -200) {
 | |
|             // Swipe vers le haut : plier
 | |
|             setState(() {
 | |
|               _calendarCollapsed = true;
 | |
|             });
 | |
|           } else if (details.primaryVelocity! > 200) {
 | |
|             // Swipe vers le bas : déplier
 | |
|             setState(() {
 | |
|               _calendarCollapsed = false;
 | |
|             });
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|       onHorizontalDragEnd: (details) {
 | |
|         if (details.primaryVelocity != null) {
 | |
|           if (details.primaryVelocity! < -200) {
 | |
|             // Swipe gauche : mois suivant
 | |
|             setState(() {
 | |
|               _focusedDay =
 | |
|                   DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
 | |
|             });
 | |
|           } else if (details.primaryVelocity! > 200) {
 | |
|             // Swipe droite : mois précédent
 | |
|             setState(() {
 | |
|               _focusedDay =
 | |
|                   DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
 | |
|             });
 | |
|           }
 | |
|         }
 | |
|       },
 | |
|       child: Stack(
 | |
|         children: [
 | |
|           // Calendrier + détails en dessous
 | |
|           AnimatedPositioned(
 | |
|             duration: const Duration(milliseconds: 400),
 | |
|             curve: Curves.easeInOut,
 | |
|             top: _calendarCollapsed ? -600 : 0, // cache le calendrier en haut
 | |
|             left: 0,
 | |
|             right: 0,
 | |
|             height: _calendarCollapsed ? 0 : null,
 | |
|             child: SizedBox(
 | |
|               height: MediaQuery.of(context).size.height,
 | |
|               child: Column(
 | |
|                 children: [
 | |
|                   _buildMonthHeader(context),
 | |
|                   if (!_calendarCollapsed)
 | |
|                     // Ajout d'un GestureDetector pour swipe horizontal sur le calendrier
 | |
|                     GestureDetector(
 | |
|                       onHorizontalDragEnd: (details) {
 | |
|                         if (details.primaryVelocity != null) {
 | |
|                           if (details.primaryVelocity! < -200) {
 | |
|                             // Swipe gauche : mois suivant
 | |
|                             setState(() {
 | |
|                               _focusedDay = DateTime(
 | |
|                                   _focusedDay.year, _focusedDay.month + 1, 1);
 | |
|                             });
 | |
|                           } else if (details.primaryVelocity! > 200) {
 | |
|                             // Swipe droite : mois précédent
 | |
|                             setState(() {
 | |
|                               _focusedDay = DateTime(
 | |
|                                   _focusedDay.year, _focusedDay.month - 1, 1);
 | |
|                             });
 | |
|                           }
 | |
|                         }
 | |
|                       },
 | |
|                       child: MobileCalendarView(
 | |
|                         focusedDay: _focusedDay,
 | |
|                         selectedDay: _selectedDay,
 | |
|                         events: eventProvider.events,
 | |
|                         onDaySelected: (day) {
 | |
|                           final eventsForDay = eventProvider.events
 | |
|                               .where((e) =>
 | |
|                                   e.startDateTime.year == day.year &&
 | |
|                                   e.startDateTime.month == day.month &&
 | |
|                                   e.startDateTime.day == day.day)
 | |
|                               .toList()
 | |
|                             ..sort((a, b) =>
 | |
|                                 a.startDateTime.compareTo(b.startDateTime));
 | |
|                           setState(() {
 | |
|                             _selectedDay = day;
 | |
|                             _calendarCollapsed = false;
 | |
|                             _selectedEventIndex = 0;
 | |
|                             _selectedEvent = eventsForDay.isNotEmpty
 | |
|                                 ? eventsForDay[0]
 | |
|                                 : null;
 | |
|                           });
 | |
|                         },
 | |
|                       ),
 | |
|                     ),
 | |
|                   Expanded(
 | |
|                     child: hasEvents
 | |
|                         // Ajout d'un GestureDetector pour swipe horizontal sur le détail événement
 | |
|                         ? GestureDetector(
 | |
|                             onHorizontalDragEnd: (details) {
 | |
|                               if (details.primaryVelocity != null) {
 | |
|                                 if (details.primaryVelocity! < -200) {
 | |
|                                   // Swipe gauche : événement suivant
 | |
|                                   if (_selectedEventIndex <
 | |
|                                       eventsForSelectedDay.length - 1) {
 | |
|                                     setState(() {
 | |
|                                       _selectedEventIndex++;
 | |
|                                       _selectedEvent = eventsForSelectedDay[
 | |
|                                           _selectedEventIndex];
 | |
|                                     });
 | |
|                                   }
 | |
|                                 } else if (details.primaryVelocity! > 200) {
 | |
|                                   // Swipe droite : événement précédent
 | |
|                                   if (_selectedEventIndex > 0) {
 | |
|                                     setState(() {
 | |
|                                       _selectedEventIndex--;
 | |
|                                       _selectedEvent = eventsForSelectedDay[
 | |
|                                           _selectedEventIndex];
 | |
|                                     });
 | |
|                                   }
 | |
|                                 }
 | |
|                               }
 | |
|                             },
 | |
|                             child: EventDetails(
 | |
|                               event: eventsForSelectedDay[_selectedEventIndex],
 | |
|                               selectedDate: _selectedDay,
 | |
|                               events: eventsForSelectedDay.cast<EventModel>(),
 | |
|                               onSelectEvent: (event, date) {
 | |
|                                 final idx = eventsForSelectedDay
 | |
|                                     .indexWhere((e) => e.id == event.id);
 | |
|                                 setState(() {
 | |
|                                   _selectedEventIndex = idx >= 0 ? idx : 0;
 | |
|                                   _selectedEvent = event;
 | |
|                                 });
 | |
|                               },
 | |
|                             ),
 | |
|                           )
 | |
|                         : Center(
 | |
|                             child: Text(
 | |
|                                 'Aucun événement ne démarre à cette date')),
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|           ),
 | |
|           // Vue détail (prend tout l'espace quand calendrier caché)
 | |
|           if (_calendarCollapsed && _selectedDay != null)
 | |
|             AnimatedPositioned(
 | |
|               duration: const Duration(milliseconds: 400),
 | |
|               curve: Curves.easeInOut,
 | |
|               top: _calendarCollapsed ? 0 : 600,
 | |
|               left: 0,
 | |
|               right: 0,
 | |
|               bottom: 0,
 | |
|               child: SizedBox(
 | |
|                 height: MediaQuery.of(context).size.height,
 | |
|                 child: Column(
 | |
|                   children: [
 | |
|                     _buildMonthHeader(context),
 | |
|                     Expanded(
 | |
|                       child: Stack(
 | |
|                         children: [
 | |
|                           if (currentEvent != null)
 | |
|                             // Ajout d'un GestureDetector pour swipe horizontal sur le détail événement
 | |
|                             GestureDetector(
 | |
|                               onHorizontalDragEnd: (details) {
 | |
|                                 if (details.primaryVelocity != null) {
 | |
|                                   if (details.primaryVelocity! < -200) {
 | |
|                                     // Swipe gauche : événement suivant
 | |
|                                     if (_selectedEventIndex <
 | |
|                                         eventsForSelectedDay.length - 1) {
 | |
|                                       setState(() {
 | |
|                                         _selectedEventIndex++;
 | |
|                                         _selectedEvent = eventsForSelectedDay[
 | |
|                                             _selectedEventIndex];
 | |
|                                       });
 | |
|                                     }
 | |
|                                   } else if (details.primaryVelocity! > 200) {
 | |
|                                     // Swipe droite : événement précédent
 | |
|                                     if (_selectedEventIndex > 0) {
 | |
|                                       setState(() {
 | |
|                                         _selectedEventIndex--;
 | |
|                                         _selectedEvent = eventsForSelectedDay[
 | |
|                                             _selectedEventIndex];
 | |
|                                       });
 | |
|                                     }
 | |
|                                   }
 | |
|                                 }
 | |
|                               },
 | |
|                               child: EventDetails(
 | |
|                                 event: currentEvent,
 | |
|                                 selectedDate: _selectedDay,
 | |
|                                 events: eventsForSelectedDay.cast<EventModel>(),
 | |
|                                 onSelectEvent: (event, date) {
 | |
|                                   final idx = eventsForSelectedDay
 | |
|                                       .indexWhere((e) => e.id == event.id);
 | |
|                                   setState(() {
 | |
|                                     _selectedEventIndex = idx >= 0 ? idx : 0;
 | |
|                                     _selectedEvent = event;
 | |
|                                   });
 | |
|                                 },
 | |
|                               ),
 | |
|                             ),
 | |
|                           if (!hasEvents)
 | |
|                             Center(
 | |
|                               child: Text(
 | |
|                                   'Aucun événement ne démarre à cette date'),
 | |
|                             ),
 | |
|                         ],
 | |
|                       ),
 | |
|                     ),
 | |
|                   ],
 | |
|                 ),
 | |
|               ),
 | |
|             ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   Widget _buildMonthHeader(BuildContext context) {
 | |
|     return Padding(
 | |
|       padding: const EdgeInsets.only(top: 8, bottom: 8),
 | |
|       child: Row(
 | |
|         children: [
 | |
|           IconButton(
 | |
|             icon: const Icon(Icons.chevron_left,
 | |
|                 color: AppColors.rouge, size: 28),
 | |
|             onPressed: () {
 | |
|               setState(() {
 | |
|                 _focusedDay =
 | |
|                     DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
 | |
|               });
 | |
|             },
 | |
|           ),
 | |
|           Expanded(
 | |
|             child: GestureDetector(
 | |
|               onTap: () {
 | |
|                 setState(() {
 | |
|                   _calendarCollapsed = !_calendarCollapsed;
 | |
|                 });
 | |
|               },
 | |
|               child: Row(
 | |
|                 mainAxisAlignment: MainAxisAlignment.center,
 | |
|                 mainAxisSize: MainAxisSize.min,
 | |
|                 children: [
 | |
|                   Text(
 | |
|                     _getMonthName(_focusedDay.month),
 | |
|                     style: const TextStyle(
 | |
|                       color: AppColors.rouge,
 | |
|                       fontWeight: FontWeight.bold,
 | |
|                       fontSize: 20,
 | |
|                     ),
 | |
|                   ),
 | |
|                   const SizedBox(width: 6),
 | |
|                   Icon(
 | |
|                     _calendarCollapsed
 | |
|                         ? Icons.keyboard_arrow_down
 | |
|                         : Icons.keyboard_arrow_up,
 | |
|                     color: AppColors.rouge,
 | |
|                     size: 26,
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|           ),
 | |
|           IconButton(
 | |
|             icon: const Icon(Icons.chevron_right,
 | |
|                 color: AppColors.rouge, size: 28),
 | |
|             onPressed: () {
 | |
|               setState(() {
 | |
|                 _focusedDay =
 | |
|                     DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
 | |
|               });
 | |
|             },
 | |
|           ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   String _getMonthName(int month) {
 | |
|     switch (month) {
 | |
|       case 1:
 | |
|         return 'Janvier';
 | |
|       case 2:
 | |
|         return 'Février';
 | |
|       case 3:
 | |
|         return 'Mars';
 | |
|       case 4:
 | |
|         return 'Avril';
 | |
|       case 5:
 | |
|         return 'Mai';
 | |
|       case 6:
 | |
|         return 'Juin';
 | |
|       case 7:
 | |
|         return 'Juillet';
 | |
|       case 8:
 | |
|         return 'Août';
 | |
|       case 9:
 | |
|         return 'Septembre';
 | |
|       case 10:
 | |
|         return 'Octobre';
 | |
|       case 11:
 | |
|         return 'Novembre';
 | |
|       case 12:
 | |
|         return 'Décembre';
 | |
|       default:
 | |
|         return '';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Widget _buildCalendar() {
 | |
|     final eventProvider = Provider.of<EventProvider>(context);
 | |
| 
 | |
|     if (_calendarFormat == CalendarFormat.week) {
 | |
|       return WeekView(
 | |
|         focusedDay: _focusedDay,
 | |
|         events: eventProvider.events,
 | |
|         onWeekChange: _changeWeek,
 | |
|         onEventSelected: (event) {
 | |
|           setState(() {
 | |
|             _selectedEvent = event;
 | |
|             _selectedDay = event.startDateTime;
 | |
|           });
 | |
|         },
 | |
|         onSwitchToMonth: () {
 | |
|           setState(() {
 | |
|             _calendarFormat = CalendarFormat.month;
 | |
|           });
 | |
|         },
 | |
|         onDaySelected: (selectedDay) {
 | |
|           final eventsForDay = eventProvider.events
 | |
|               .where((e) =>
 | |
|                   e.startDateTime.year == selectedDay.year &&
 | |
|                   e.startDateTime.month == selectedDay.month &&
 | |
|                   e.startDateTime.day == selectedDay.day)
 | |
|               .toList();
 | |
|           eventsForDay
 | |
|               .sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|           setState(() {
 | |
|             _selectedDay = selectedDay;
 | |
|             if (eventsForDay.isNotEmpty) {
 | |
|               _selectedEvent = eventsForDay.first;
 | |
|             } else {
 | |
|               _selectedEvent = null;
 | |
|             }
 | |
|           });
 | |
|           if (eventsForDay.isEmpty) {
 | |
|             ScaffoldMessenger.of(context).showSnackBar(
 | |
|               const SnackBar(
 | |
|                 content: Text("Aucun événement ne démarre à cette date."),
 | |
|                 duration: Duration(seconds: 2),
 | |
|               ),
 | |
|             );
 | |
|           }
 | |
|         },
 | |
|         selectedEvent: _selectedEvent,
 | |
|       );
 | |
|     } else {
 | |
|       return MonthView(
 | |
|         focusedDay: _focusedDay,
 | |
|         selectedDay: _selectedDay,
 | |
|         calendarFormat: _calendarFormat,
 | |
|         events: eventProvider.events,
 | |
|         onDaySelected: (selectedDay, focusedDay) {
 | |
|           final eventsForDay = eventProvider.events
 | |
|               .where((event) =>
 | |
|                   event.startDateTime.year == selectedDay.year &&
 | |
|                   event.startDateTime.month == selectedDay.month &&
 | |
|                   event.startDateTime.day == selectedDay.day)
 | |
|               .toList()
 | |
|             ..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|           setState(() {
 | |
|             _selectedDay = selectedDay;
 | |
|             _focusedDay = focusedDay;
 | |
|             if (eventsForDay.isNotEmpty) {
 | |
|               _selectedEvent = eventsForDay.first;
 | |
|             } else {
 | |
|               _selectedEvent = null;
 | |
|             }
 | |
|           });
 | |
|         },
 | |
|         onFormatChanged: (format) {
 | |
|           setState(() {
 | |
|             _calendarFormat = format;
 | |
|           });
 | |
|         },
 | |
|         onPageChanged: (focusedDay) {
 | |
|           setState(() {
 | |
|             _focusedDay = focusedDay;
 | |
|           });
 | |
|         },
 | |
|         onEventSelected: (event) {
 | |
|           setState(() {
 | |
|             _selectedEvent = event;
 | |
|           });
 | |
|         },
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| }
 | 
