import 'package:comunic/lists/forez_presences_set.dart'; import 'package:comunic/utils/date_utils.dart' as date_utils; /// Forez presence calendar widget /// /// This widget is used only by Forez groups /// /// @author Pierre Hubert import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; enum CalendarDisplayMode { SINGLE_USER, MULTIPLE_USERS } extension DateOnlyCompare on DateTime { bool isSameDate(DateTime? other) => date_utils.isSameDate(this, other); } class PresenceCalendarWidget extends StatefulWidget { final PresenceSet presenceSet; final void Function(DateTime)? onDayClicked; final CalendarDisplayMode mode; final DateTime? selectedDay; const PresenceCalendarWidget({ Key? key, required this.presenceSet, this.onDayClicked, this.mode = CalendarDisplayMode.SINGLE_USER, this.selectedDay, }) : super(key: key); @override _PresenceCalendarWidgetState createState() => _PresenceCalendarWidgetState(); } class _PresenceCalendarWidgetState extends State { DateTime? selectedDay = DateTime.now(); @override void didUpdateWidget(covariant PresenceCalendarWidget oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.selectedDay != widget.selectedDay) { selectedDay = widget.selectedDay; setState(() {}); } } @override Widget build(BuildContext context) { return TableCalendar( firstDay: DateTime.utc(2020, 01, 01), lastDay: DateTime.now().add(Duration(days: 365 * 2)), selectedDayPredicate: (d) => d == selectedDay, locale: "fr_FR", weekendDays: [], onHeaderTapped: _pickDate, calendarBuilders: CalendarBuilders( defaultBuilder: _dayBuilder, todayBuilder: _dayBuilder, selectedBuilder: _dayBuilder, ), onDaySelected: _selectedDay, availableCalendarFormats: const {CalendarFormat.month: "Mois"}, focusedDay: selectedDay!, onPageChanged: (s) { setState(() { selectedDay = s; }); }, ); } void _pickDate(DateTime date) async { final pickedDate = await showDatePicker( context: context, initialDate: date, firstDate: DateTime.now().subtract(Duration(days: 20)), lastDate: DateTime.now().add(Duration(days: 365 * 5)), ); setState(() { if (pickedDate != null) selectedDay = pickedDate; }); } Widget _dayBuilder(BuildContext context, DateTime date, DateTime focusedDay) { if (widget.presenceSet.containsDate(date)) { // Show the number of users who are present if (widget.mode == CalendarDisplayMode.MULTIPLE_USERS) return Stack( children: [ CellWidget( text: date.day.toString(), color: Colors.green, textColor: Colors.white, circle: false, selected: date.isSameDate(widget.selectedDay), ), Positioned( child: Material( child: Padding( padding: const EdgeInsets.all(2.0), child: Text(widget.presenceSet.countAtDate(date).toString()), ), textStyle: TextStyle(color: Colors.white), color: Colors.red, ), bottom: 4, right: 4, ), ], ); // Only show green circle else return CellWidget( text: date.day.toString(), color: Colors.green, textColor: Colors.white, ); } return CellWidget( text: date.day.toString(), selected: date.isSameDate(widget.selectedDay), ); } void _selectedDay(DateTime selecteDay, DateTime focusedDay) { if (widget.onDayClicked != null) widget.onDayClicked!(selecteDay); setState(() {}); } } class CellWidget extends StatelessWidget { final String text; final Color? color; final Color? textColor; final bool circle; final bool? selected; const CellWidget({ Key? key, required this.text, this.color, this.textColor, this.circle = true, this.selected, }) : super(key: key); @override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 250), decoration: _buildCellDecoration(), margin: const EdgeInsets.all(6.0), alignment: Alignment.center, child: Text( text, textAlign: TextAlign.center, style: TextStyle(color: selected ?? false ? Colors.white : textColor), ), ); } Decoration _buildCellDecoration() => BoxDecoration( shape: circle ? BoxShape.circle : BoxShape.rectangle, color: selected ?? false ? Colors.deepPurple : color, ); }