Line data Source code
1 : import 'package:alh_calendar/enums/day_of_week.dart';
2 : import 'package:alh_calendar/models/calendar_day.dart';
3 : import 'package:alh_calendar/models/calendar_month.dart';
4 : import 'package:alh_calendar/models/calendar_week.dart';
5 : import 'package:alh_calendar/utils/date_helper.dart';
6 : import 'package:alh_calendar/widgets/alh_calendar.dart';
7 : import 'package:alh_calendar/widgets/calendar_cell.dart';
8 : import 'package:flutter/material.dart';
9 :
10 : /// Creates a list of all days needed to show a CalendarMonth.
11 : ///
12 : /// The CalendarTableHelper need the DateTime of the month which should be shown.
13 : /// Because a month does not always start at monday and end at a sunday we would get
14 : /// empty days in front and at the end of the month. To avoid this empty days we add
15 : /// days from the previous month in front and days from the next month at the end of
16 : /// the current month:
17 : /// [daysOfPreviousMonth + daysOfCurrentMonth + daysOfNextMonth]
18 : class CalendarTableHelper {
19 4 : static int weekSize = DateTime.daysPerWeek;
20 : static int calendarWeekLength = 5;
21 :
22 2 : static CalendarMonth buildCurrentCalendarMonth({
23 : required DateTime date,
24 : required bool forceSixWeekMonth,
25 : }) {
26 2 : return CalendarMonth(
27 : weeks:
28 2 : _getCalendarWeeks(date: date, forceSixWeekMonth: forceSixWeekMonth),
29 6 : month: DateTime(date.year, date.month),
30 : );
31 : }
32 :
33 : // returns chunks of all given days as calendarWeeks
34 2 : static List<CalendarWeek> _getCalendarWeeks(
35 : {required DateTime date, required bool forceSixWeekMonth}) {
36 2 : final calendarWeeks = <CalendarWeek>[];
37 : // get all days in current month
38 2 : final daysInCurrentMonth = _getDaysInMonth(date, true);
39 :
40 : // get all days in previous month
41 : final daysInPreviousMonth =
42 10 : _getDaysInMonth(DateTime(date.year, date.month - 1), false);
43 :
44 : // get all days in next month
45 : final daysInNextMonth =
46 10 : _getDaysInMonth(DateTime(date.year, date.month + 1), false);
47 :
48 : // weekday on which the month starts
49 2 : final firstWeekDayOfMonth = _getFirstWeekDayOfMonth(date);
50 :
51 : // fills up the days in front of currtenmonth days with days from previous month
52 2 : final daysOfPreviousMonth = _fillDaysInMonthBeginning(
53 : weekday: firstWeekDayOfMonth,
54 : lastMonthDays: daysInPreviousMonth,
55 : );
56 2 : final daysOfNextMonth = _fillDaysInMonthEnd(
57 6 : weekday: daysInCurrentMonth.last.date.weekday,
58 : nextMonthDays: daysInNextMonth,
59 : );
60 4 : final days = daysOfPreviousMonth + daysInCurrentMonth + daysOfNextMonth;
61 :
62 8 : for (var i = 0; i < days.length; i += weekSize) {
63 2 : final newCalendarWeek = CalendarWeek(
64 6 : days: days.sublist(i, i + weekSize),
65 : );
66 2 : calendarWeeks.add(newCalendarWeek);
67 : }
68 :
69 4 : if (forceSixWeekMonth && calendarWeeks.length == calendarWeekLength) {
70 8 : final start = daysOfNextMonth.isEmpty ? 0 : daysOfNextMonth.last.date.day;
71 2 : final newCalendarWeek = CalendarWeek(
72 6 : days: daysInNextMonth.sublist(start, start + weekSize),
73 : );
74 2 : calendarWeeks.add(newCalendarWeek);
75 : }
76 :
77 : return calendarWeeks;
78 : }
79 :
80 : /// Returns on which day of the week the month starts
81 : ///
82 : /// for example: monday or saturday
83 2 : static int _getFirstWeekDayOfMonth(DateTime month) {
84 10 : return DateTime(month.year, month.month, month.day).weekday;
85 : }
86 :
87 : /// Depending on which weekday the month starts, we need to fill the List of days in front.
88 : ///
89 : /// For example if the month starts at wednesday, then monday and tuesday would be empty.
90 : /// In this case these days get filled with the last two days of the previous month.
91 2 : static List<CalendarDay> _fillDaysInMonthBeginning({
92 : required int weekday,
93 : required List<CalendarDay> lastMonthDays,
94 : }) {
95 2 : final lastMonthDaysLength = lastMonthDays.length;
96 2 : return lastMonthDays.sublist(
97 4 : lastMonthDaysLength - weekday + 1,
98 : lastMonthDaysLength,
99 : );
100 : }
101 :
102 : /// Depending on which weekday the month ends, days should be added at the end of the month.
103 : ///
104 : /// For example if the month ends at friday, then saturday and sunday would be empty.
105 : /// In this case these days get filled with the first two days of the next month.
106 2 : static List<CalendarDay> _fillDaysInMonthEnd({
107 : required int weekday,
108 : required List<CalendarDay> nextMonthDays,
109 : }) {
110 4 : return nextMonthDays.sublist(0, DateTime.daysPerWeek - weekday);
111 : }
112 :
113 : /// Returns a list of calendarDays
114 : ///
115 : /// For every day in the given month a clendarDay is added to the list.
116 2 : static List<CalendarDay> _getDaysInMonth(
117 : DateTime date,
118 : bool isCurrentMonth,
119 : ) {
120 6 : final totalDays = DateUtils.getDaysInMonth(date.year, date.month);
121 2 : final List<CalendarDay> dateTimeDayList = [];
122 4 : for (int i = 0; i < totalDays; i++) {
123 8 : final day = DateTime(date.year, date.month, i + 1);
124 :
125 4 : dateTimeDayList.add(CalendarDay(
126 : date: day,
127 : isInCurrentMonth: isCurrentMonth,
128 6 : dayOfWeek: DayOfWeek.values[day.weekday - 1],
129 : ));
130 : }
131 :
132 : return dateTimeDayList;
133 : }
134 :
135 : /// Returns a list with all TableRows needed.
136 : ///
137 : /// For every calendarWeek in given [CalendarMonth], one TableRow is build and
138 : /// each row gets filled with the calendarDays of the calendarWeeks.
139 3 : static List<TableRow> buildCalendarTableRow({
140 : required CalendarMonth calendarMonth,
141 : required bool disableSixthRow,
142 : required DateTime selectedDateTime,
143 : required DayBuilder dayBuilder,
144 : required ValueChanged<DateTime> onSelectDay,
145 : required bool disableClickOnOutOfRange,
146 : DateTime? minimumDayDate,
147 : DateTime? maximumDayDate,
148 : }) {
149 3 : return calendarMonth.weeks
150 9 : .map((week) => TableRow(
151 3 : children: week.days
152 9 : .map((day) => _buildCalendarCell(
153 : calendarMonth: calendarMonth,
154 : calendarDay: day,
155 : calendarWeek: week,
156 : disableSixthRow: disableSixthRow,
157 : selectedDateTime: selectedDateTime,
158 : dayBuilder: dayBuilder,
159 : onSelectDay: onSelectDay,
160 : disableClickOnOutOfRange: disableSixthRow,
161 : minimumDayDate: minimumDayDate,
162 : maximumDayDate: maximumDayDate))
163 3 : .toList()))
164 3 : .toList();
165 : }
166 :
167 3 : static CalendarCell _buildCalendarCell({
168 : required CalendarMonth calendarMonth,
169 : required CalendarDay calendarDay,
170 : required CalendarWeek calendarWeek,
171 : required bool disableSixthRow,
172 : required DateTime selectedDateTime,
173 : required DayBuilder dayBuilder,
174 : required ValueChanged<DateTime> onSelectDay,
175 : required bool disableClickOnOutOfRange,
176 : DateTime? minimumDayDate,
177 : DateTime? maximumDayDate,
178 : }) {
179 : late bool disableLastRow = false;
180 : // if disableSixthRow == true the last row has to be disabled
181 9 : if (calendarWeek == calendarMonth.weeks.last && disableSixthRow) {
182 : disableLastRow =
183 1 : !DateHelper.isDayOfCurrentMonthInLastRow(calendarWeek: calendarWeek);
184 : }
185 : // calculate if day is out of range
186 3 : final isOutOfRange = DateHelper.isDayOutOfRange(
187 3 : dayDateTime: calendarDay.date,
188 : minimumDayDate: minimumDayDate,
189 : maximumDayDate: maximumDayDate);
190 3 : return CalendarCell(
191 : isSixthRowAndDisabled: disableLastRow,
192 3 : date: calendarDay.date,
193 3 : isInCurrentMonth: calendarDay.isInCurrentMonth,
194 6 : isSelected: selectedDateTime == calendarDay.date,
195 6 : isWeekend: calendarDay.dayOfWeek.isWeekend,
196 : isOutOfRange: isOutOfRange,
197 : dayBuilder: dayBuilder,
198 : onTap: !disableClickOnOutOfRange || !isOutOfRange
199 6 : ? () => onSelectDay(calendarDay.date)
200 : : null,
201 : );
202 : }
203 : }
|