From 6adc90ecfef38a72019a36e61bb1327c159ded59 Mon Sep 17 00:00:00 2001 From: "PC-PAUL\\paulf" Date: Sun, 18 May 2025 20:34:57 +0200 Subject: [PATCH] Refacto et clean --- em2rp/lib/pages/auth/reset_password_page.dart | 8 +- em2rp/lib/utils/calendar_utils.dart | 115 +++ em2rp/lib/view_model/login_view_model.dart | 2 - em2rp/lib/views/calendar_page.dart | 777 +----------------- em2rp/lib/views/user_management_page.dart | 42 +- .../widgets/calendar_widgets/month_view.dart | 238 ++++++ .../widgets/calendar_widgets/week_view.dart | 383 +++++++++ .../views/widgets/image/big_image_left.dart | 2 +- .../image/profile_picture_selector.dart | 8 +- em2rp/lib/views/widgets/nav/main_drawer.dart | 9 +- .../widgets/user_management/user_card.dart | 4 +- em2rp/lib/widgets/custom_app_bar.dart | 30 +- em2rp/lib/widgets/event_details.dart | 4 +- 13 files changed, 830 insertions(+), 792 deletions(-) create mode 100644 em2rp/lib/utils/calendar_utils.dart create mode 100644 em2rp/lib/views/widgets/calendar_widgets/month_view.dart create mode 100644 em2rp/lib/views/widgets/calendar_widgets/week_view.dart diff --git a/em2rp/lib/pages/auth/reset_password_page.dart b/em2rp/lib/pages/auth/reset_password_page.dart index 5ed60b1..123576e 100644 --- a/em2rp/lib/pages/auth/reset_password_page.dart +++ b/em2rp/lib/pages/auth/reset_password_page.dart @@ -6,16 +6,16 @@ class ResetPasswordPage extends StatefulWidget { final String actionCode; const ResetPasswordPage({ - Key? key, + super.key, required this.email, required this.actionCode, - }) : super(key: key); + }); @override - _ResetPasswordPageState createState() => _ResetPasswordPageState(); + ResetPasswordPageState createState() => ResetPasswordPageState(); } -class _ResetPasswordPageState extends State { +class ResetPasswordPageState extends State { final _formKey = GlobalKey(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); diff --git a/em2rp/lib/utils/calendar_utils.dart b/em2rp/lib/utils/calendar_utils.dart new file mode 100644 index 0000000..409377a --- /dev/null +++ b/em2rp/lib/utils/calendar_utils.dart @@ -0,0 +1,115 @@ +import 'package:em2rp/models/event_model.dart'; + +class CalendarUtils { + static String getDayName(int weekday) { + switch (weekday) { + case DateTime.monday: + return 'Lundi'; + case DateTime.tuesday: + return 'Mardi'; + case DateTime.wednesday: + return 'Mercredi'; + case DateTime.thursday: + return 'Jeudi'; + case DateTime.friday: + return 'Vendredi'; + case DateTime.saturday: + return 'Samedi'; + case DateTime.sunday: + return 'Dimanche'; + default: + return ''; + } + } + + static String getShortDayName(int weekday) { + switch (weekday) { + case DateTime.monday: + return 'Lun'; + case DateTime.tuesday: + return 'Mar'; + case DateTime.wednesday: + return 'Mer'; + case DateTime.thursday: + return 'Jeu'; + case DateTime.friday: + return 'Ven'; + case DateTime.saturday: + return 'Sam'; + case DateTime.sunday: + return 'Dim'; + default: + return ''; + } + } + + static 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 ''; + } + } + + static String getMonthYearString(DateTime weekStart, DateTime weekEnd) { + if (weekStart.month == weekEnd.month) { + return '${getMonthName(weekStart.month)} ${weekStart.year}'; + } else { + return '${getMonthName(weekStart.month)} - ${getMonthName(weekEnd.month)} ${weekEnd.year}'; + } + } + + static bool isMultiDayEvent(EventModel event) { + return event.startDateTime.day != event.endDateTime.day || + event.startDateTime.month != event.endDateTime.month || + event.startDateTime.year != event.endDateTime.year; + } + + static int calculateTotalDays(EventModel event) { + final startDate = DateTime(event.startDateTime.year, + event.startDateTime.month, event.startDateTime.day); + final endDate = DateTime( + event.endDateTime.year, event.endDateTime.month, event.endDateTime.day); + return endDate.difference(startDate).inDays + 1; + } + + static int calculateDayNumber(DateTime startDate, DateTime currentDay) { + final start = DateTime(startDate.year, startDate.month, startDate.day); + final current = DateTime(currentDay.year, currentDay.month, currentDay.day); + return current.difference(start).inDays + 1; + } + + static List getEventsForDay( + DateTime day, List events) { + final dayStart = DateTime(day.year, day.month, day.day, 0, 0); + final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59); + + return events.where((event) { + return !(event.endDateTime.isBefore(dayStart) || + event.startDateTime.isAfter(dayEnd)); + }).toList(); + } +} diff --git a/em2rp/lib/view_model/login_view_model.dart b/em2rp/lib/view_model/login_view_model.dart index 5416a76..0bf842f 100644 --- a/em2rp/lib/view_model/login_view_model.dart +++ b/em2rp/lib/view_model/login_view_model.dart @@ -27,8 +27,6 @@ class LoginViewModel extends ChangeNotifier { notifyListeners(); try { - final userCredential = await localAuthProvider.signInWithEmailAndPassword( - emailController.text.trim(), passwordController.text); print('User signed in'); // Attendre que les données utilisateur soient chargées diff --git a/em2rp/lib/views/calendar_page.dart b/em2rp/lib/views/calendar_page.dart index 243ee5a..1ec53d7 100644 --- a/em2rp/lib/views/calendar_page.dart +++ b/em2rp/lib/views/calendar_page.dart @@ -4,16 +4,15 @@ import 'package:flutter/material.dart'; import 'package:em2rp/widgets/custom_app_bar.dart'; import 'package:em2rp/views/widgets/nav/main_drawer.dart'; import 'package:provider/provider.dart'; -import 'package:em2rp/utils/colors.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:em2rp/models/event_model.dart'; import 'package:em2rp/widgets/event_details.dart'; -import 'package:latlong2/latlong.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:em2rp/views/widgets/calendar_widgets/month_view.dart'; +import 'package:em2rp/views/widgets/calendar_widgets/week_view.dart'; class CalendarPage extends StatefulWidget { - const CalendarPage({Key? key}) : super(key: key); + const CalendarPage({super.key}); @override State createState() => _CalendarPageState(); @@ -51,7 +50,6 @@ class _CalendarPageState extends State { @override Widget build(BuildContext context) { - final localAuthProvider = Provider.of(context); final eventProvider = Provider.of(context); final isMobile = MediaQuery.of(context).size.width < 600; @@ -111,41 +109,30 @@ class _CalendarPageState extends State { } Widget _buildCalendar() { - return LayoutBuilder( - builder: (context, constraints) { - if (_calendarFormat == CalendarFormat.week) { - return _buildWeekView(constraints); - } else { - return _buildMonthView(constraints); - } - }, - ); - } + final eventProvider = Provider.of(context); - Widget _buildMonthView(BoxConstraints constraints) { - // Calculer la hauteur des lignes en fonction de la hauteur disponible - // Ajustement pour les mois à 6 semaines - final rowHeight = (constraints.maxHeight - 100) / - 6; // Augmenté de 80 à 100 pour donner plus d'espace - - return Container( - height: constraints.maxHeight, - padding: - const EdgeInsets.all(8), // Réduit de 16 à 8 pour gagner de l'espace - child: TableCalendar( - firstDay: DateTime.utc(2020, 1, 1), - lastDay: DateTime.utc(2030, 12, 31), + if (_calendarFormat == CalendarFormat.week) { + return WeekView( focusedDay: _focusedDay, + events: eventProvider.events, + onWeekChange: _changeWeek, + onEventSelected: (event) { + setState(() { + _selectedEvent = event; + }); + }, + onSwitchToMonth: () { + setState(() { + _calendarFormat = CalendarFormat.month; + }); + }, + ); + } else { + return MonthView( + focusedDay: _focusedDay, + selectedDay: _selectedDay, calendarFormat: _calendarFormat, - startingDayOfWeek: StartingDayOfWeek.monday, - locale: 'fr_FR', - availableCalendarFormats: const { - CalendarFormat.month: 'Mois', - CalendarFormat.week: 'Semaine', - }, - selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); - }, + events: eventProvider.events, onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; @@ -158,714 +145,16 @@ class _CalendarPageState extends State { }); }, onPageChanged: (focusedDay) { - _focusedDay = focusedDay; + setState(() { + _focusedDay = focusedDay; + }); }, - calendarStyle: CalendarStyle( - defaultDecoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(4), - ), - selectedDecoration: BoxDecoration( - color: AppColors.rouge, - border: Border.all(color: AppColors.rouge), - borderRadius: BorderRadius.circular(4), - ), - todayDecoration: BoxDecoration( - color: AppColors.rouge.withOpacity(0.1), - border: Border.all(color: AppColors.rouge), - borderRadius: BorderRadius.circular(4), - ), - outsideDecoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(4), - ), - outsideDaysVisible: false, - cellMargin: EdgeInsets.zero, - cellPadding: EdgeInsets.zero, - ), - rowHeight: rowHeight, - headerStyle: HeaderStyle( - formatButtonVisible: true, - titleCentered: true, - formatButtonShowsNext: false, - formatButtonDecoration: BoxDecoration( - color: AppColors.rouge, - borderRadius: BorderRadius.circular(16), - ), - formatButtonTextStyle: const TextStyle(color: Colors.white), - leftChevronIcon: - const Icon(Icons.chevron_left, color: AppColors.rouge), - rightChevronIcon: - const Icon(Icons.chevron_right, color: AppColors.rouge), - headerPadding: const EdgeInsets.symmetric(vertical: 8), - titleTextStyle: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - calendarBuilders: CalendarBuilders( - dowBuilder: (context, day) { - return Center( - child: Text( - _getDayName(day.weekday), - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ); - }, - defaultBuilder: (context, day, focusedDay) { - final events = _getEventsForDay(day); - - return Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(4), - ), - child: Stack( - children: [ - // Numéro du jour en haut à gauche - Positioned( - top: 4, - left: 4, - child: Text( - day.day.toString(), - style: TextStyle( - color: - isSameDay(day, _selectedDay) ? Colors.white : null, - ), - ), - ), - // Badge du nombre d'événements en haut à droite - if (events.isNotEmpty) - Positioned( - top: 4, - right: 4, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: isSameDay(day, _selectedDay) - ? Colors.white - : AppColors.rouge, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - events.length.toString(), - style: TextStyle( - color: isSameDay(day, _selectedDay) - ? AppColors.rouge - : Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - // Liste des événements en dessous - if (events.isNotEmpty) - Positioned( - bottom: 2, - left: 2, - right: 2, - top: 28, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: events - .map((event) => GestureDetector( - onTap: () { - setState(() { - _selectedEvent = event; - _selectedDay = day; - }); - }, - child: Container( - margin: const EdgeInsets.only(bottom: 2), - padding: const EdgeInsets.symmetric( - horizontal: 4, vertical: 2), - decoration: BoxDecoration( - color: AppColors.rouge.withOpacity(0.1), - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - event.name, - style: const TextStyle( - fontSize: 12, - color: AppColors.rouge, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (_isMultiDayEvent(event)) - Text( - 'Jour ${_calculateDayNumber(event.startDateTime, day)}/${_calculateTotalDays(event)}', - style: const TextStyle( - fontSize: 10, - color: AppColors.rouge, - ), - maxLines: 1, - ), - ], - ), - ), - )) - .toList(), - ), - ), - ), - ], - ), - ); - }, - selectedBuilder: (context, day, focusedDay) { - final events = _getEventsForDay(day); - - return Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: AppColors.rouge, - border: Border.all(color: AppColors.rouge), - borderRadius: BorderRadius.circular(4), - ), - child: Stack( - children: [ - // Numéro du jour en haut à gauche - Positioned( - top: 4, - left: 4, - child: Text( - day.day.toString(), - style: const TextStyle(color: Colors.white), - ), - ), - // Badge du nombre d'événements en haut à droite - if (events.isNotEmpty) - Positioned( - top: 4, - right: 4, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - events.length.toString(), - style: const TextStyle( - color: AppColors.rouge, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - // Liste des événements en dessous - if (events.isNotEmpty) - Positioned( - bottom: 2, - left: 2, - right: 2, - top: 28, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: events - .map((event) => GestureDetector( - onTap: () { - setState(() { - _selectedEvent = event; - }); - }, - child: Container( - margin: const EdgeInsets.only(bottom: 2), - padding: const EdgeInsets.symmetric( - horizontal: 4, vertical: 2), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - event.name, - style: const TextStyle( - fontSize: 12, - color: Colors.white, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (_isMultiDayEvent(event)) - Text( - 'Jour ${_calculateDayNumber(event.startDateTime, day)}/${_calculateTotalDays(event)}', - style: const TextStyle( - fontSize: 10, - color: Colors.white, - ), - maxLines: 1, - ), - ], - ), - ), - )) - .toList(), - ), - ), - ), - ], - ), - ); - }, - ), - ), - ); - } - - Widget _buildWeekView(BoxConstraints constraints) { - final weekStart = - _focusedDay.subtract(Duration(days: _focusedDay.weekday - 1)); - final weekEnd = weekStart.add(const Duration(days: 6)); - - // Ajustement de la hauteur pour éviter l'overflow - double availableHeight = - constraints.maxHeight - 80; // Réserver de l'espace pour les en-têtes - final hourHeight = availableHeight / 24; - final dayWidth = (constraints.maxWidth - 50) / 7; - - // Préparer les événements par jour (en tenant compte des multi-jours) - List> eventsByDay = List.generate(7, (i) => []); - for (final event in Provider.of(context).events) { - // Pour chaque jour de la semaine - for (int i = 0; i < 7; i++) { - final day = weekStart.add(Duration(days: i)); - final dayStart = DateTime(day.year, day.month, day.day, 0, 0); - final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59); - // Si l'événement recouvre ce jour - if (!(event.endDateTime.isBefore(dayStart) || - event.startDateTime.isAfter(dayEnd))) { - // Tronquer les heures de début/fin si besoin - final start = event.startDateTime.isBefore(dayStart) - ? dayStart - : event.startDateTime; - final end = - event.endDateTime.isAfter(dayEnd) ? dayEnd : event.endDateTime; - eventsByDay[i].add(_PositionedEvent(event, start, end)); - } - } - } - - // Pour chaque jour, calculer les "colonnes" d'événements qui se chevauchent - List> eventsWithColumnsByDay = []; - for (final dayEvents in eventsByDay) { - final columns = _assignColumns(dayEvents); - eventsWithColumnsByDay.add(columns); - } - - return Column( - children: [ - // Barre d'en-tête semaine - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - icon: const Icon(Icons.chevron_left), - onPressed: () => _changeWeek(-1), - ), - Text( - _getMonthYearString(weekStart, weekEnd), - style: - const TextStyle(fontWeight: FontWeight.bold, fontSize: 18), - ), - Row( - children: [ - TextButton( - onPressed: () { - setState(() { - _calendarFormat = CalendarFormat.month; - }); - }, - style: TextButton.styleFrom( - backgroundColor: AppColors.rouge, - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - ), - child: const Text('Semaine'), - ), - IconButton( - icon: const Icon(Icons.chevron_right), - onPressed: () => _changeWeek(1), - ), - ], - ), - ], - ), - ), - // En-tête avec les jours - SizedBox( - height: 40, - child: Row( - children: [ - Container( - width: 50, - color: Colors.transparent, - ), - ...List.generate(7, (index) { - final day = weekStart.add(Duration(days: index)); - return Container( - width: dayWidth, - decoration: BoxDecoration( - border: Border( - right: BorderSide( - color: index < 6 - ? Colors.grey.shade300 - : Colors.transparent, - width: 1, - ), - ), - ), - child: Stack( - children: [ - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(_getDayName(day.weekday), - style: const TextStyle( - fontWeight: FontWeight.bold)), - Text('${day.day}', - style: const TextStyle(fontSize: 13)), - ], - ), - ), - if (_getEventsForDay(day).isNotEmpty) - Positioned( - top: 4, - right: 4, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: AppColors.rouge, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - _getEventsForDay(day).length.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ), - ); - }), - ], - ), - ), - // Grille des heures + jours - Expanded( - child: SingleChildScrollView( - child: SizedBox( - height: 24 * hourHeight, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Colonne des heures - Column( - children: List.generate(24, (index) { - return Container( - width: 50, - height: hourHeight, - alignment: Alignment.topRight, - padding: const EdgeInsets.only(right: 4), - child: Text( - '${index.toString().padLeft(2, '0')}:00', - style: TextStyle( - color: Colors.grey.shade600, - fontSize: 12, - ), - ), - ); - }), - ), - // Grille des jours - Expanded( - child: Stack( - children: [ - // Lignes horizontales - Column( - children: List.generate(24, (index) { - return Container( - height: hourHeight, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.grey.shade300, - width: 0.5, - ), - ), - ), - ); - }), - ), - // Bordures verticales entre jours - Positioned.fill( - child: Row( - children: List.generate(7, (i) { - return Container( - width: dayWidth, - decoration: BoxDecoration( - border: Border( - right: BorderSide( - color: i < 6 - ? Colors.grey.shade300 - : Colors.transparent, - width: 1, - ), - ), - ), - ); - }), - ), - ), - // Événements (chevauchements et multi-jours) - ...List.generate(7, (dayIdx) { - final dayEvents = eventsWithColumnsByDay[dayIdx]; - return Stack( - children: dayEvents.map((e) { - final startHour = - e.start.hour + e.start.minute / 60; - final endHour = e.end.hour + e.end.minute / 60; - final duration = endHour - startHour; - final width = dayWidth / e.totalColumns; - return Positioned( - left: dayIdx * dayWidth + e.column * width, - top: startHour * hourHeight, - width: width, - height: duration * hourHeight, - child: GestureDetector( - onTap: () { - setState(() { - _selectedEvent = e.event; - _selectedDay = - weekStart.add(Duration(days: dayIdx)); - }); - }, - child: Container( - margin: const EdgeInsets.all(2), - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: AppColors.rouge.withOpacity(0.2), - border: - Border.all(color: AppColors.rouge), - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - e.event.name, - style: const TextStyle( - color: AppColors.rouge, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (_isMultiDayEvent(e.event)) - Text( - 'Jour ${_calculateDayNumber(e.event.startDateTime, weekStart.add(Duration(days: dayIdx)))}/${_calculateTotalDays(e.event)}', - style: const TextStyle( - color: AppColors.rouge, - fontSize: 10, - ), - maxLines: 1, - ), - ], - ), - ), - ), - ); - }).toList(), - ); - }), - ], - ), - ), - ], - ), - ), - ), - ), - ], - ); - } - - // Méthode pour récupérer les événements pour un jour donné (inclut les multi-jours) - List _getEventsForDay(DateTime day) { - final dayStart = DateTime(day.year, day.month, day.day, 0, 0); - final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59); - - return Provider.of(context).events.where((event) { - return !(event.endDateTime.isBefore(dayStart) || - event.startDateTime.isAfter(dayEnd)); - }).toList(); - } - - // Méthodes pour gérer les événements multi-jours - bool _isMultiDayEvent(EventModel event) { - return event.startDateTime.day != event.endDateTime.day || - event.startDateTime.month != event.endDateTime.month || - event.startDateTime.year != event.endDateTime.year; - } - - int _calculateTotalDays(EventModel event) { - final startDate = DateTime(event.startDateTime.year, - event.startDateTime.month, event.startDateTime.day); - final endDate = DateTime( - event.endDateTime.year, event.endDateTime.month, event.endDateTime.day); - return endDate.difference(startDate).inDays + 1; - } - - int _calculateDayNumber(DateTime startDate, DateTime currentDay) { - final start = DateTime(startDate.year, startDate.month, startDate.day); - final current = DateTime(currentDay.year, currentDay.month, currentDay.day); - return current.difference(start).inDays + 1; - } - - String _getDayName(int weekday) { - switch (weekday) { - case DateTime.monday: - return 'Lun'; - case DateTime.tuesday: - return 'Mar'; - case DateTime.wednesday: - return 'Mer'; - case DateTime.thursday: - return 'Jeu'; - case DateTime.friday: - return 'Ven'; - case DateTime.saturday: - return 'Sam'; - case DateTime.sunday: - return 'Dim'; - default: - return ''; - } - } - - 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 ''; - } - } - - String _getMonthYearString(DateTime weekStart, DateTime weekEnd) { - if (weekStart.month == weekEnd.month) { - return '${_getMonthName(weekStart.month)} ${weekStart.year}'; - } else { - return '${_getMonthName(weekStart.month)} - ${_getMonthName(weekEnd.month)} ${weekEnd.year}'; + onEventSelected: (event) { + setState(() { + _selectedEvent = event; + }); + }, + ); } } } - -class _PositionedEvent { - final EventModel event; - final DateTime start; - final DateTime end; - _PositionedEvent(this.event, this.start, this.end); -} - -class _PositionedEventWithColumn extends _PositionedEvent { - final int column; - final int totalColumns; - _PositionedEventWithColumn(EventModel event, DateTime start, DateTime end, - this.column, this.totalColumns) - : super(event, start, end); -} - -List<_PositionedEventWithColumn> _assignColumns(List<_PositionedEvent> events) { - // Algorithme simple : - // - Trier par heure de début - // - Pour chaque événement, trouver la première colonne libre - // - Attribuer le nombre total de colonnes pour le groupe de chevauchement - events.sort((a, b) => a.start.compareTo(b.start)); - List<_PositionedEventWithColumn> result = []; - List> columns = []; - for (final e in events) { - bool placed = false; - for (int col = 0; col < columns.length; col++) { - if (columns[col].isEmpty || !(_overlap(columns[col].last, e))) { - columns[col] - .add(_PositionedEventWithColumn(e.event, e.start, e.end, col, 0)); - placed = true; - break; - } - } - if (!placed) { - columns.add([ - _PositionedEventWithColumn(e.event, e.start, e.end, columns.length, 0) - ]); - } - } - // Mettre à jour le nombre total de colonnes pour chaque événement - int totalCols = columns.length; - for (final col in columns) { - for (final e in col) { - result.add(_PositionedEventWithColumn( - e.event, e.start, e.end, e.column, totalCols)); - } - } - return result; -} - -bool _overlap(_PositionedEvent a, _PositionedEvent b) { - return a.end.isAfter(b.start) && a.start.isBefore(b.end); -} diff --git a/em2rp/lib/views/user_management_page.dart b/em2rp/lib/views/user_management_page.dart index 933142c..c5cd43b 100644 --- a/em2rp/lib/views/user_management_page.dart +++ b/em2rp/lib/views/user_management_page.dart @@ -11,7 +11,7 @@ import 'package:em2rp/models/role_model.dart'; import 'package:em2rp/widgets/custom_app_bar.dart'; class UserManagementPage extends StatefulWidget { - const UserManagementPage({Key? key}) : super(key: key); + const UserManagementPage({super.key}); @override State createState() => _UserManagementPageState(); @@ -21,8 +21,11 @@ class _UserManagementPageState extends State { @override void initState() { super.initState(); - Future.microtask( - () => Provider.of(context, listen: false).fetchUsers()); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + Provider.of(context, listen: false).fetchUsers(); + } + }); } @override @@ -242,24 +245,29 @@ class _UserManagementPageState extends State { profilePhotoUrl: '', ); + final scaffoldMessenger = ScaffoldMessenger.of(context); await Provider.of(context, listen: false) .createUserWithEmailInvite(newUser); - Navigator.pop(context); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Invitation envoyée avec succès'), - backgroundColor: Colors.green, - ), - ); + if (context.mounted) { + Navigator.pop(context); + scaffoldMessenger.showSnackBar( + const SnackBar( + content: Text('Invitation envoyée avec succès'), + backgroundColor: Colors.green, + ), + ); + } } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Erreur lors de la création: ${e.toString()}'), - backgroundColor: Colors.red, - ), - ); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Erreur lors de la création: ${e.toString()}'), + backgroundColor: Colors.red, + ), + ); + } } }, style: ElevatedButton.styleFrom( diff --git a/em2rp/lib/views/widgets/calendar_widgets/month_view.dart b/em2rp/lib/views/widgets/calendar_widgets/month_view.dart new file mode 100644 index 0000000..f3695b9 --- /dev/null +++ b/em2rp/lib/views/widgets/calendar_widgets/month_view.dart @@ -0,0 +1,238 @@ +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import 'package:em2rp/utils/colors.dart'; +import 'package:em2rp/models/event_model.dart'; +import 'package:em2rp/utils/calendar_utils.dart'; + +class MonthView extends StatelessWidget { + final DateTime focusedDay; + final DateTime? selectedDay; + final CalendarFormat calendarFormat; + final Function(DateTime, DateTime) onDaySelected; + final Function(CalendarFormat) onFormatChanged; + final Function(DateTime) onPageChanged; + final List events; + final Function(EventModel) onEventSelected; + + const MonthView({ + super.key, + required this.focusedDay, + required this.selectedDay, + required this.calendarFormat, + required this.onDaySelected, + required this.onFormatChanged, + required this.onPageChanged, + required this.events, + required this.onEventSelected, + }); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final rowHeight = (constraints.maxHeight - 100) / 6; + + return Container( + height: constraints.maxHeight, + padding: const EdgeInsets.all(8), + child: TableCalendar( + firstDay: DateTime.utc(2020, 1, 1), + lastDay: DateTime.utc(2030, 12, 31), + focusedDay: focusedDay, + calendarFormat: calendarFormat, + startingDayOfWeek: StartingDayOfWeek.monday, + locale: 'fr_FR', + availableCalendarFormats: const { + CalendarFormat.month: 'Mois', + CalendarFormat.week: 'Semaine', + }, + selectedDayPredicate: (day) => isSameDay(selectedDay, day), + onDaySelected: onDaySelected, + onFormatChanged: onFormatChanged, + onPageChanged: onPageChanged, + calendarStyle: _buildCalendarStyle(), + rowHeight: rowHeight, + headerStyle: _buildHeaderStyle(), + calendarBuilders: _buildCalendarBuilders(), + ), + ); + }, + ); + } + + CalendarStyle _buildCalendarStyle() { + return CalendarStyle( + defaultDecoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(4), + ), + selectedDecoration: BoxDecoration( + color: AppColors.rouge, + border: Border.all(color: AppColors.rouge), + borderRadius: BorderRadius.circular(4), + ), + todayDecoration: BoxDecoration( + color: AppColors.rouge.withAlpha(26), + border: Border.all(color: AppColors.rouge), + borderRadius: BorderRadius.circular(4), + ), + outsideDecoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(4), + ), + outsideDaysVisible: false, + cellMargin: EdgeInsets.zero, + cellPadding: EdgeInsets.zero, + ); + } + + HeaderStyle _buildHeaderStyle() { + return HeaderStyle( + formatButtonVisible: true, + titleCentered: true, + formatButtonShowsNext: false, + formatButtonDecoration: BoxDecoration( + color: AppColors.rouge, + borderRadius: BorderRadius.circular(16), + ), + formatButtonTextStyle: const TextStyle(color: Colors.white), + leftChevronIcon: const Icon(Icons.chevron_left, color: AppColors.rouge), + rightChevronIcon: const Icon(Icons.chevron_right, color: AppColors.rouge), + headerPadding: const EdgeInsets.symmetric(vertical: 8), + titleTextStyle: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ); + } + + CalendarBuilders _buildCalendarBuilders() { + return CalendarBuilders( + dowBuilder: (context, day) { + return Center( + child: Text( + CalendarUtils.getShortDayName(day.weekday), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ); + }, + defaultBuilder: (context, day, focusedDay) { + return _buildDayCell(day, false); + }, + selectedBuilder: (context, day, focusedDay) { + return _buildDayCell(day, true); + }, + ); + } + + Widget _buildDayCell(DateTime day, bool isSelected) { + final dayEvents = CalendarUtils.getEventsForDay(day, events); + final textColor = isSelected ? Colors.white : null; + final badgeColor = isSelected ? Colors.white : AppColors.rouge; + final badgeTextColor = isSelected ? AppColors.rouge : Colors.white; + + return Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: isSelected ? AppColors.rouge : null, + border: Border.all( + color: isSelected ? AppColors.rouge : Colors.grey.shade300, + ), + borderRadius: BorderRadius.circular(4), + ), + child: Stack( + children: [ + Positioned( + top: 4, + left: 4, + child: Text( + day.day.toString(), + style: TextStyle(color: textColor), + ), + ), + if (dayEvents.isNotEmpty) + Positioned( + top: 4, + right: 4, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: badgeColor, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + dayEvents.length.toString(), + style: TextStyle( + color: badgeTextColor, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + if (dayEvents.isNotEmpty) + Positioned( + bottom: 2, + left: 2, + right: 2, + top: 28, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: dayEvents + .map((event) => _buildEventItem(event, isSelected, day)) + .toList(), + ), + ), + ), + ], + ), + ); + } + + Widget _buildEventItem( + EventModel event, bool isSelected, DateTime currentDay) { + return GestureDetector( + onTap: () { + onDaySelected(currentDay, currentDay); + onEventSelected(event); + }, + child: Container( + margin: const EdgeInsets.only(bottom: 2), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + decoration: BoxDecoration( + color: isSelected + ? Colors.white.withAlpha(51) + : AppColors.rouge.withAlpha(26), + borderRadius: BorderRadius.circular(4), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + event.name, + style: TextStyle( + fontSize: 12, + color: isSelected ? Colors.white : AppColors.rouge, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + if (CalendarUtils.isMultiDayEvent(event)) + Text( + 'Jour ${CalendarUtils.calculateDayNumber(event.startDateTime, event.startDateTime)}/${CalendarUtils.calculateTotalDays(event)}', + style: TextStyle( + fontSize: 10, + color: isSelected ? Colors.white : AppColors.rouge, + ), + maxLines: 1, + ), + ], + ), + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/calendar_widgets/week_view.dart b/em2rp/lib/views/widgets/calendar_widgets/week_view.dart new file mode 100644 index 0000000..143d5e5 --- /dev/null +++ b/em2rp/lib/views/widgets/calendar_widgets/week_view.dart @@ -0,0 +1,383 @@ +import 'package:flutter/material.dart'; +import 'package:em2rp/utils/colors.dart'; +import 'package:em2rp/models/event_model.dart'; +import 'package:em2rp/utils/calendar_utils.dart'; + +class WeekView extends StatelessWidget { + final DateTime focusedDay; + final List events; + final Function(int) onWeekChange; + final Function(EventModel) onEventSelected; + final Function() onSwitchToMonth; + + const WeekView({ + super.key, + required this.focusedDay, + required this.events, + required this.onWeekChange, + required this.onEventSelected, + required this.onSwitchToMonth, + }); + + @override + Widget build(BuildContext context) { + final weekStart = + focusedDay.subtract(Duration(days: focusedDay.weekday - 1)); + final weekEnd = weekStart.add(const Duration(days: 6)); + + return Column( + children: [ + _buildWeekHeader(weekStart, weekEnd), + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + final availableHeight = constraints.maxHeight - 80; + final hourHeight = availableHeight / 24; + final dayWidth = (constraints.maxWidth - 50) / 7; + + return SingleChildScrollView( + child: SizedBox( + height: 24 * hourHeight, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHourColumn(hourHeight), + Expanded( + child: _buildWeekGrid( + weekStart, + hourHeight, + dayWidth, + constraints, + ), + ), + ], + ), + ), + ); + }, + ), + ), + ], + ); + } + + Widget _buildWeekHeader(DateTime weekStart, DateTime weekEnd) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: const Icon(Icons.chevron_left), + onPressed: () => onWeekChange(-1), + ), + Text( + CalendarUtils.getMonthYearString(weekStart, weekEnd), + style: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + ), + Row( + children: [ + TextButton( + onPressed: onSwitchToMonth, + style: TextButton.styleFrom( + backgroundColor: AppColors.rouge, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + ), + child: const Text('Semaine'), + ), + IconButton( + icon: const Icon(Icons.chevron_right), + onPressed: () => onWeekChange(1), + ), + ], + ), + ], + ), + ), + _buildDaysHeader(weekStart), + ], + ); + } + + Widget _buildDaysHeader(DateTime weekStart) { + return SizedBox( + height: 40, + child: Row( + children: [ + Container( + width: 50, + color: Colors.transparent, + ), + ...List.generate(7, (index) { + final day = weekStart.add(Duration(days: index)); + return Expanded( + child: Container( + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: + index < 6 ? Colors.grey.shade300 : Colors.transparent, + width: 1, + ), + ), + ), + child: Stack( + children: [ + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + CalendarUtils.getShortDayName(day.weekday), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text( + '${day.day}', + style: const TextStyle(fontSize: 13), + ), + ], + ), + ), + if (CalendarUtils.getEventsForDay(day, events).isNotEmpty) + Positioned( + top: 4, + right: 4, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: AppColors.rouge, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + CalendarUtils.getEventsForDay(day, events) + .length + .toString(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ); + }), + ], + ), + ); + } + + Widget _buildHourColumn(double hourHeight) { + return Column( + children: List.generate(24, (index) { + return Container( + width: 50, + height: hourHeight, + alignment: Alignment.topRight, + padding: const EdgeInsets.only(right: 4), + child: Text( + '${index.toString().padLeft(2, '0')}:00', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 12, + ), + ), + ); + }), + ); + } + + Widget _buildWeekGrid( + DateTime weekStart, + double hourHeight, + double dayWidth, + BoxConstraints constraints, + ) { + final eventsByDay = _prepareEventsByDay(weekStart); + final eventsWithColumnsByDay = _assignColumnsToEvents(eventsByDay); + + return Stack( + children: [ + // Lignes horizontales + Column( + children: List.generate(24, (index) { + return Container( + height: hourHeight, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey.shade300, + width: 0.5, + ), + ), + ), + ); + }), + ), + // Bordures verticales entre jours + Positioned.fill( + child: Row( + children: List.generate(7, (i) { + return Container( + width: dayWidth, + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: i < 6 ? Colors.grey.shade300 : Colors.transparent, + width: 1, + ), + ), + ), + ); + }), + ), + ), + // Événements + ...List.generate(7, (dayIdx) { + final dayEvents = eventsWithColumnsByDay[dayIdx]; + return Stack( + children: dayEvents.map((e) { + final startHour = e.start.hour + e.start.minute / 60; + final endHour = e.end.hour + e.end.minute / 60; + final duration = endHour - startHour; + final width = dayWidth / e.totalColumns; + + return Positioned( + left: dayIdx * dayWidth + e.column * width, + top: startHour * hourHeight, + width: width, + height: duration * hourHeight, + child: GestureDetector( + onTap: () => onEventSelected(e.event), + child: Container( + margin: const EdgeInsets.all(2), + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: AppColors.rouge.withAlpha(26), + border: Border.all(color: AppColors.rouge), + borderRadius: BorderRadius.circular(4), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + e.event.name, + style: const TextStyle( + color: AppColors.rouge, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + if (CalendarUtils.isMultiDayEvent(e.event)) + Text( + 'Jour ${CalendarUtils.calculateDayNumber(e.event.startDateTime, weekStart.add(Duration(days: dayIdx)))}/${CalendarUtils.calculateTotalDays(e.event)}', + style: const TextStyle( + color: AppColors.rouge, + fontSize: 10, + ), + maxLines: 1, + ), + ], + ), + ), + ), + ); + }).toList(), + ); + }), + ], + ); + } + + List> _prepareEventsByDay(DateTime weekStart) { + List> eventsByDay = List.generate(7, (i) => []); + + for (final event in events) { + for (int i = 0; i < 7; i++) { + final day = weekStart.add(Duration(days: i)); + final dayStart = DateTime(day.year, day.month, day.day, 0, 0); + final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59); + + if (!(event.endDateTime.isBefore(dayStart) || + event.startDateTime.isAfter(dayEnd))) { + final start = event.startDateTime.isBefore(dayStart) + ? dayStart + : event.startDateTime; + final end = + event.endDateTime.isAfter(dayEnd) ? dayEnd : event.endDateTime; + eventsByDay[i].add(_PositionedEvent(event, start, end)); + } + } + } + + return eventsByDay; + } + + List> _assignColumnsToEvents( + List> eventsByDay) { + return eventsByDay.map((dayEvents) { + dayEvents.sort((a, b) => a.start.compareTo(b.start)); + List<_PositionedEventWithColumn> result = []; + List> columns = []; + + for (final e in dayEvents) { + bool placed = false; + for (int col = 0; col < columns.length; col++) { + if (columns[col].isEmpty || !_overlap(columns[col].last, e)) { + columns[col].add( + _PositionedEventWithColumn(e.event, e.start, e.end, col, 0)); + placed = true; + break; + } + } + if (!placed) { + columns.add([ + _PositionedEventWithColumn( + e.event, e.start, e.end, columns.length, 0) + ]); + } + } + + int totalCols = columns.length; + for (final col in columns) { + for (final e in col) { + result.add(_PositionedEventWithColumn( + e.event, e.start, e.end, e.column, totalCols)); + } + } + return result; + }).toList(); + } + + bool _overlap(_PositionedEvent a, _PositionedEvent b) { + return a.end.isAfter(b.start) && a.start.isBefore(b.end); + } +} + +class _PositionedEvent { + final EventModel event; + final DateTime start; + final DateTime end; + _PositionedEvent(this.event, this.start, this.end); +} + +class _PositionedEventWithColumn extends _PositionedEvent { + final int column; + final int totalColumns; + _PositionedEventWithColumn( + super.event, super.start, super.end, this.column, this.totalColumns); +} diff --git a/em2rp/lib/views/widgets/image/big_image_left.dart b/em2rp/lib/views/widgets/image/big_image_left.dart index 0e450db..5611f31 100644 --- a/em2rp/lib/views/widgets/image/big_image_left.dart +++ b/em2rp/lib/views/widgets/image/big_image_left.dart @@ -7,7 +7,7 @@ class BigLeftImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: AppColors.gris.withOpacity(0.1), + color: AppColors.gris.withAlpha(26), child: ClipRRect( borderRadius: BorderRadius.zero, child: Image.asset( diff --git a/em2rp/lib/views/widgets/image/profile_picture_selector.dart b/em2rp/lib/views/widgets/image/profile_picture_selector.dart index 060608e..41c3e0a 100644 --- a/em2rp/lib/views/widgets/image/profile_picture_selector.dart +++ b/em2rp/lib/views/widgets/image/profile_picture_selector.dart @@ -15,12 +15,12 @@ class _ProfilePictureSelectorState extends State { bool _isHovering = false; Future _pickAndUploadImage() async { + if (!context.mounted) return; + final provider = Provider.of(context, listen: false); final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); - if (image != null) { - // Envoie l'image au provider - await Provider.of(context, listen: false) - .changeProfilePicture(image); + if (image != null && context.mounted) { + await provider.changeProfilePicture(image); } } diff --git a/em2rp/lib/views/widgets/nav/main_drawer.dart b/em2rp/lib/views/widgets/nav/main_drawer.dart index 4a3dad7..1533af7 100644 --- a/em2rp/lib/views/widgets/nav/main_drawer.dart +++ b/em2rp/lib/views/widgets/nav/main_drawer.dart @@ -13,9 +13,9 @@ class MainDrawer extends StatelessWidget { final String currentPage; const MainDrawer({ - Key? key, + super.key, required this.currentPage, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -33,7 +33,7 @@ class MainDrawer extends StatelessWidget { image: const AssetImage('assets/EM2_NsurB.jpg'), fit: BoxFit.cover, colorFilter: ColorFilter.mode( - AppColors.noir.withOpacity(0.4), + AppColors.noir.withAlpha(102), BlendMode.darken, ), ), @@ -88,7 +88,8 @@ class MainDrawer extends StatelessWidget { Navigator.pop(context); Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) => const CalendarPage()), + MaterialPageRoute( + builder: (context) => const CalendarPage()), ); }, ), diff --git a/em2rp/lib/views/widgets/user_management/user_card.dart b/em2rp/lib/views/widgets/user_management/user_card.dart index a742382..c63ff43 100644 --- a/em2rp/lib/views/widgets/user_management/user_card.dart +++ b/em2rp/lib/views/widgets/user_management/user_card.dart @@ -10,11 +10,11 @@ class UserCard extends StatelessWidget { static const double _desktopMaxWidth = 280; const UserCard({ - Key? key, + super.key, required this.user, required this.onEdit, required this.onDelete, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/em2rp/lib/widgets/custom_app_bar.dart b/em2rp/lib/widgets/custom_app_bar.dart index b045b64..131bcce 100644 --- a/em2rp/lib/widgets/custom_app_bar.dart +++ b/em2rp/lib/widgets/custom_app_bar.dart @@ -3,25 +3,33 @@ import 'package:provider/provider.dart'; import 'package:em2rp/providers/local_user_provider.dart'; import 'package:em2rp/utils/colors.dart'; -class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { +class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { final String title; final List? actions; final bool showLogoutButton; const CustomAppBar({ - Key? key, + super.key, required this.title, this.actions, this.showLogoutButton = true, - }) : super(key: key); + }); + @override + State createState() => _CustomAppBarState(); + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} + +class _CustomAppBarState extends State { @override Widget build(BuildContext context) { return AppBar( - title: Text(title), + title: Text(widget.title), backgroundColor: AppColors.rouge, actions: [ - if (showLogoutButton) + if (widget.showLogoutButton) IconButton( icon: const Icon(Icons.logout, color: AppColors.blanc), onPressed: () async { @@ -45,21 +53,19 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ), ); - if (shouldLogout == true) { + if (shouldLogout == true && context.mounted) { // Déconnexion - await Provider.of(context, listen: false) - .signOut(); + final provider = + Provider.of(context, listen: false); + await provider.signOut(); if (context.mounted) { Navigator.of(context).pushReplacementNamed('/login'); } } }, ), - if (actions != null) ...actions!, + if (widget.actions != null) ...widget.actions!, ], ); } - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); } diff --git a/em2rp/lib/widgets/event_details.dart b/em2rp/lib/widgets/event_details.dart index 60c49f4..ef56549 100644 --- a/em2rp/lib/widgets/event_details.dart +++ b/em2rp/lib/widgets/event_details.dart @@ -7,9 +7,9 @@ class EventDetails extends StatelessWidget { final EventModel event; const EventDetails({ - Key? key, + super.key, required this.event, - }) : super(key: key); + }); @override Widget build(BuildContext context) {