LCOV - code coverage report
Current view: top level - widgets - alh_calendar.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 74 80 92.5 %
Date: 2022-10-19 14:19:28 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:alh_calendar/enums/day_of_week.dart';
       2             : import 'package:alh_calendar/models/calendar_day_builder_model.dart';
       3             : import 'package:alh_calendar/models/calendar_month.dart';
       4             : import 'package:alh_calendar/utils/calendar_table_helper.dart';
       5             : import 'package:alh_calendar/utils/date_helper.dart';
       6             : import 'package:alh_calendar/widgets/calendar_body.dart';
       7             : import 'package:alh_calendar/widgets/calendar_header/calendar_header.dart';
       8             : import 'package:flutter/material.dart';
       9             : 
      10             : /// Signature for a function that builds a widget for a given day.
      11             : typedef DayBuilder = Widget Function(
      12             :   CalendarDayBuilderModel calendarDayBuilderModel,
      13             : );
      14             : 
      15             : /// Signature for a function that builds a widget for a title.
      16             : typedef HeaderTitleBuilder = Widget Function(
      17             :   DateTime date,
      18             : );
      19             : 
      20             : /// Signature for a function that builds a widget for the day of the week
      21             : /// calendarCell.
      22             : typedef DayOfWeekBuilder = Widget Function(
      23             :   String dayOfWeek,
      24             :   bool isWeekEnd,
      25             : );
      26             : 
      27             : class AlhCalendar extends StatefulWidget {
      28             :   /// Custom builder for DayCalendarCells.
      29             :   ///
      30             :   /// Uses a [CalendarDayBuilderModel]
      31             :   final DayBuilder dayBuilder;
      32             : 
      33             :   /// Custom builder for the headerTitle.
      34             :   final HeaderTitleBuilder headerBuilder;
      35             : 
      36             :   /// custom widget that is left of header
      37             :   final Widget headerLeading;
      38             : 
      39             :   /// custom widget that is right of header
      40             :   final Widget headerTrailing;
      41             : 
      42             :   /// Custom builder for DayOfWeekCalendarCells.
      43             :   final DayOfWeekBuilder dayOfWeekBuilder;
      44             : 
      45             :   /// Defines initial Time, if null than DateTime.now()
      46             :   final DateTime? initialDateTime;
      47             : 
      48             :   /// Defines the minimum month.
      49             :   ///
      50             :   /// If minimum month is reached, user cannot go to previous month.
      51             :   final DateTime? minimumMonthDate;
      52             : 
      53             :   /// Defines the maximum month.
      54             :   ///
      55             :   /// If maximum month is reached, user cannot go to next month.
      56             :   final DateTime? maximumMonthDate;
      57             : 
      58             :   /// Sets the minimum day.
      59             :   ///
      60             :   /// Every day before chosen day is flagged as outside of Range.
      61             :   /// All days in between minimum day and maximum day are called in range. The [minimumMonthDate]
      62             :   /// and [minimumDayDate] have nothing in commom, [minimumMonthDate] sets the lowest month
      63             :   /// the user can navigate to, while [minimumDayDate] sets the first day of the Range.
      64             :   final DateTime? minimumDayDate;
      65             : 
      66             :   /// Sets the maximum day.
      67             :   ///
      68             :   /// All days after maximum day are flagged as outside of Range.
      69             :   /// All days in between minimum day and maximum day are called in range.
      70             :   /// The [maximumMonthDate] and [maximumDayDate] have nothing in commom, [maximumMonthDate] sets the last month
      71             :   /// the user can navigate to, while [maximumDayDate] sets the last day of the Range.
      72             :   final DateTime? maximumDayDate;
      73             : 
      74             :   /// needs a Map of <DayOfWeek, String> to fill DayOfWeekCalendarCells
      75             :   ///
      76             :   /// example:
      77             :   /// final dayOfWeekMap = {
      78             :   ///     DayOfWeek.monday: 'Mon',
      79             :   ///     DayOfWeek.thursday: 'Thu',
      80             :   ///     DayOfWeek.wednesday: 'Wen',
      81             :   ///     DayOfWeek.tuesday: 'Tue',
      82             :   ///     DayOfWeek.friday: 'Fri',
      83             :   ///     DayOfWeek.saturday: 'Sat',
      84             :   ///     DayOfWeek.sunday: 'Sun',
      85             :   ///   };
      86             :   final Map<DayOfWeek, String> daysOfWeek;
      87             : 
      88             :   /// Callback once minimum month is reached
      89             :   final VoidCallback? onReachedMinimumDate;
      90             : 
      91             :   /// Callback once maximum month is reached
      92             :   final VoidCallback? onReachedMaximumDate;
      93             : 
      94             :   /// Callback once month is changed
      95             :   final ValueChanged<DateTime>? onMonthChanged;
      96             : 
      97             :   /// Callback once day is changed
      98             :   final ValueChanged<DateTime>? onDayChanged;
      99             : 
     100             :   /// Flag if horizontal scrolling in calendar is enabled.
     101             :   ///
     102             :   /// Default value: true
     103             :   final bool? enableHorizontalSwipe;
     104             : 
     105             :   /// Sets the padding between headerTitle and both headerIcons
     106             :   ///
     107             :   /// Default value: EdgeInsets.all(3.0)
     108             :   final EdgeInsets iconPadding;
     109             : 
     110             :   /// Enables changing the month when a day outside of current month is tapped.
     111             :   ///
     112             :   /// Default value: true
     113             :   final bool enableJumpToOtherMonth;
     114             : 
     115             :   /// Forces Table to have 6 Rows, even when month would fit in 5.
     116             :   ///
     117             :   /// Helpful if any widget is placed in a column under AlhCalender that should not move up and down when month is changed.
     118             :   /// Default value: true
     119             :   final bool enableSixWeeksForEveryMonth;
     120             : 
     121             :   /// Makes sixth Row invisible if there are only days from next month shown in the last row.
     122             :   ///
     123             :   /// Default value: true
     124             :   final bool disableSixthRow;
     125             : 
     126             :   /// Should show days that are not in current month.
     127             :   ///
     128             :   /// If true days outside of current month are shown. If false the days are shown,
     129             :   /// but still flagged with isInCurrentMonth = false, so
     130             :   /// it can be used depending on the situation.
     131             :   ///
     132             :   /// Default value: true
     133             :   final bool showDaysOutsideCurrentMonth;
     134             : 
     135             :   /// If true days out of range can't be tapped
     136             :   ///
     137             :   /// Default value: true
     138             :   final bool disableTapOnOutOfRange;
     139             : 
     140           1 :   const AlhCalendar({
     141             :     required this.dayBuilder,
     142             :     required this.headerBuilder,
     143             :     required this.headerLeading,
     144             :     required this.headerTrailing,
     145             :     required this.dayOfWeekBuilder,
     146             :     required this.daysOfWeek,
     147             :     this.initialDateTime,
     148             :     this.maximumMonthDate,
     149             :     this.minimumMonthDate,
     150             :     this.minimumDayDate,
     151             :     this.maximumDayDate,
     152             :     this.onReachedMinimumDate,
     153             :     this.onReachedMaximumDate,
     154             :     this.onMonthChanged,
     155             :     this.onDayChanged,
     156             :     this.iconPadding = const EdgeInsets.all(3.0),
     157             :     this.enableHorizontalSwipe = true,
     158             :     this.enableSixWeeksForEveryMonth = true,
     159             :     this.showDaysOutsideCurrentMonth = true,
     160             :     this.disableTapOnOutOfRange = true,
     161             :     this.disableSixthRow = false,
     162             :     this.enableJumpToOtherMonth = true,
     163             :     super.key,
     164           3 :   }) : assert(daysOfWeek.length == 7);
     165             : 
     166           1 :   @override
     167           1 :   State<AlhCalendar> createState() => _AlhCalendarState();
     168             : }
     169             : 
     170             : class _AlhCalendarState extends State<AlhCalendar> {
     171             :   late CalendarMonth calendarMonth;
     172             :   late DateTime selectedDateTime;
     173             :   late DateTime currentDateTime;
     174             : 
     175           1 :   @override
     176             :   void initState() {
     177           1 :     super.initState();
     178           3 :     this.currentDateTime = this.widget.initialDateTime ?? DateTime.now();
     179           2 :     this.selectedDateTime = this.currentDateTime;
     180           2 :     this.calendarMonth = CalendarTableHelper.buildCurrentCalendarMonth(
     181           5 :       date: DateTime(this.currentDateTime.year, this.currentDateTime.month),
     182           2 :       forceSixWeekMonth: this.widget.enableSixWeeksForEveryMonth,
     183             :     );
     184             :   }
     185             : 
     186             :   /// Handles press on headerTrailing.
     187             :   ///
     188             :   /// Callback after isMaximumMonthDateReached is true and returns without doing anything
     189             :   /// If maximum month is not reached yet, [currentDateTime] and [calendarMonth] gets
     190             :   /// updated with next month. Also Callback after month is changed.
     191           1 :   void _goToNextMonth() {
     192           2 :     if (this.widget.maximumMonthDate != null &&
     193           1 :         DateHelper.isMaximumMonthDateReached(
     194           2 :             maximumDateTime: this.widget.maximumMonthDate!,
     195           1 :             currentDateTime: this.currentDateTime)) {
     196           3 :       this.widget.onReachedMaximumDate?.call();
     197             :       return;
     198             :     }
     199             : 
     200           2 :     setState(() {
     201           1 :       this.currentDateTime =
     202           6 :           DateTime(this.currentDateTime.year, this.currentDateTime.month + 1);
     203           2 :       this.calendarMonth = CalendarTableHelper.buildCurrentCalendarMonth(
     204           1 :           date: this.currentDateTime,
     205           2 :           forceSixWeekMonth: this.widget.enableSixWeeksForEveryMonth);
     206             : 
     207           4 :       this.widget.onMonthChanged?.call(this.currentDateTime);
     208             :     });
     209             :   }
     210             : 
     211             :   /// Handles tap on headerLeading.
     212             :   ///
     213             :   /// Callback if isMinimumMonthDateReached is reached and returns without doing
     214             :   /// anything. IF isMinimumMonthDateReached is false, updates [currentDateTime] and
     215             :   /// [calendarMonth] with previous month. Also Callback after changing the month.
     216           1 :   void _goToPreviousMonth() {
     217           2 :     if (this.widget.minimumMonthDate != null &&
     218           1 :         DateHelper.isMinimumMonthDateReached(
     219           2 :             minimumMonthDate: this.widget.minimumMonthDate!,
     220           1 :             currentDateTime: this.currentDateTime)) {
     221           3 :       this.widget.onReachedMinimumDate?.call();
     222             :       return;
     223             :     }
     224             : 
     225           2 :     setState(() {
     226           1 :       this.currentDateTime =
     227           6 :           DateTime(this.currentDateTime.year, this.currentDateTime.month - 1);
     228           2 :       this.calendarMonth = CalendarTableHelper.buildCurrentCalendarMonth(
     229           1 :         date: this.currentDateTime,
     230           2 :         forceSixWeekMonth: this.widget.enableSixWeeksForEveryMonth,
     231             :       );
     232           2 :       this.widget.onMonthChanged?.call(this.currentDateTime);
     233             :     });
     234             :   }
     235             : 
     236             :   /// Handles tap on a day.
     237             :   ///
     238             :   /// If enableJumpToOtherMonth is true and selected day is outside of
     239             :   /// the current month, then the calendar will jump to the month where the
     240             :   /// selected day is in.
     241             :   /// Either way [selectedDateTime] gets updated with selectedDate. Also
     242             :   /// Callback after day is changed.
     243           1 :   void _selectCalendarDay(DateTime date) {
     244           2 :     if (date != this.selectedDateTime) {
     245           2 :       if (this.widget.enableJumpToOtherMonth) {
     246           5 :         if (date.month == 12 && this.currentDateTime.month == 1) {
     247           1 :           this._goToPreviousMonth();
     248           0 :         } else if (date.month == 1 && this.currentDateTime.month == 12) {
     249           0 :           this._goToPreviousMonth();
     250           0 :         } else if (date.month > this.currentDateTime.month) {
     251           0 :           this._goToNextMonth();
     252           0 :         } else if (date.month < this.currentDateTime.month) {
     253           0 :           this._goToPreviousMonth();
     254             :         }
     255             :       }
     256             : 
     257           2 :       setState(() {
     258           1 :         this.selectedDateTime = date;
     259           2 :         this.widget.onDayChanged?.call(this.selectedDateTime);
     260             :       });
     261             :     }
     262             :   }
     263             : 
     264             :   /// Calculates the swiping direction and calls method depending on
     265             :   /// the outcome of calculation.
     266           1 :   void _onHorizontalDragEnd(DragEndDetails details) {
     267           4 :     if (details.velocity.pixelsPerSecond.dx < 0) {
     268           1 :       this._goToNextMonth();
     269           4 :     } else if (details.velocity.pixelsPerSecond.dx > 0) {
     270           1 :       this._goToPreviousMonth();
     271             :     }
     272             :   }
     273             : 
     274           1 :   @override
     275             :   Widget build(BuildContext context) {
     276           1 :     return GestureDetector(
     277             :       behavior: HitTestBehavior.opaque,
     278           3 :       onHorizontalDragEnd: this.widget.enableHorizontalSwipe == true
     279           1 :           ? this._onHorizontalDragEnd
     280             :           : null,
     281           1 :       child: Column(
     282             :         crossAxisAlignment: CrossAxisAlignment.start,
     283           1 :         children: [
     284           1 :           CalenderHeader(
     285           1 :             onPressedNext: this._goToNextMonth,
     286           1 :             onPressedPrevious: this._goToPreviousMonth,
     287           4 :             header: this.widget.headerBuilder(this.currentDateTime),
     288           2 :             padding: this.widget.iconPadding,
     289           2 :             iconLeft: this.widget.headerLeading,
     290           2 :             iconRight: this.widget.headerTrailing,
     291             :           ),
     292           1 :           CalendarBody(
     293           1 :             onSelectDay: this._selectCalendarDay,
     294           1 :             calendarMonth: this.calendarMonth,
     295           2 :             dayBuilder: this.widget.dayBuilder,
     296           2 :             dayOfWeekBuilder: this.widget.dayOfWeekBuilder,
     297           2 :             daysOfWeek: this.widget.daysOfWeek,
     298           1 :             selectedDateTime: this.selectedDateTime,
     299           2 :             minimumDayDate: this.widget.minimumDayDate,
     300           2 :             maximumDayDate: this.widget.maximumDayDate,
     301           2 :             disableClickOnOutOfRange: this.widget.disableTapOnOutOfRange,
     302           2 :             disableSixthRow: this.widget.disableSixthRow,
     303             :           ),
     304             :         ],
     305             :       ),
     306             :     );
     307             :   }
     308             : }

Generated by: LCOV version 1.16