Passage en vue mobile du calendrier

This commit is contained in:
2025-05-29 12:41:43 +02:00
parent 77d0d5cc81
commit 004d442e67
4 changed files with 395 additions and 38 deletions

View File

@ -11,6 +11,8 @@ 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/pages/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});
@ -24,12 +26,40 @@ class _CalendarPageState extends State<CalendarPage> {
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();
events.sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
int closestIdx = 0;
Duration minDiff = (events[0].startDateTime.difference(now)).abs();
for (int i = 1; i < events.length; i++) {
final diff = (events[i].startDateTime.difference(now)).abs();
if (diff < minDiff) {
minDiff = diff;
closestIdx = i;
}
}
final closestEvent = events[closestIdx];
setState(() {
_selectedDay = DateTime(closestEvent.startDateTime.year,
closestEvent.startDateTime.month, closestEvent.startDateTime.day);
_focusedDay = _selectedDay!;
_selectedEventIndex = 0;
_selectedEvent = closestEvent;
});
}
});
}
Future<void> _loadEvents() async {
@ -69,8 +99,23 @@ class _CalendarPageState extends State<CalendarPage> {
}
return Scaffold(
appBar: const CustomAppBar(
title: 'Calendrier',
appBar: CustomAppBar(
title: _getMonthName(_focusedDay.month),
actions: [
IconButton(
icon: Icon(
_calendarCollapsed
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_up,
color: AppColors.blanc,
),
onPressed: () {
setState(() {
_calendarCollapsed = !_calendarCollapsed;
});
},
),
],
),
drawer: const MainDrawer(currentPage: '/calendar'),
body: isMobile ? _buildMobileLayout() : _buildDesktopLayout(),
@ -128,37 +173,221 @@ class _CalendarPageState extends State<CalendarPage> {
Widget _buildMobileLayout() {
final eventProvider = Provider.of<EventProvider>(context);
return Column(
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;
return Stack(
children: [
// Calendrier
Expanded(
child: _buildCalendar(),
),
// Détails de l'événement
if (_selectedEvent != null)
Expanded(
child: EventDetails(
event: _selectedEvent!,
selectedDate: _selectedDay,
events: eventProvider.events,
onSelectEvent: (event, date) {
setState(() {
_selectedEvent = event;
_selectedDay = date;
});
},
// 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: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
_buildMonthHeader(context),
if (!_calendarCollapsed)
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
? 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')),
),
],
),
),
if (_selectedEvent == null && _selectedDay != null)
Expanded(
child: 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: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
_buildMonthHeader(context),
Expanded(
child: Stack(
children: [
if (currentEvent != null)
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);