-
Notifications
You must be signed in to change notification settings - Fork 0
/
nb_calendar.dart
232 lines (193 loc) · 6.66 KB
/
nb_calendar.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
part of nhabe;
class NBCalendar extends StatefulWidget {
/// custom builder for the header title. e.g. changing the date format
/// default: yMMM format
final CalendarTitleBuilder titleBuilder;
/// whether to show the calendar header
/// default: true
final bool showHeader;
/// switch between two different UIs for month picker (simple view & grid view)
/// default: grid view
final MonthPickerMode monthPickerMode;
/// current month & year
/// default: current month
final MonthAndYear selectedMonthAndYear;
/// selected date
/// default: today
final DateTime selectedDate;
/// map of: <day_of_current_month , num_of_related_items>
final Map<Date, int> dayEventIndicator;
/// color of the event indicator.
/// default: primary theme color
final Color eventIndicatorColor;
/// callback for month-changed event
final MonthChangedCallBack onMonthChanged;
/// callback for date-selected event
final OnDateSelected onDateSelected;
/// label for the weekdays
/// default: M, T, W, T, F, S, S
final Map<int, String> weekDayLabels;
/// show the days from previous / next month
/// default: true
final bool showInActiveMonthDays;
/// draw a circle around the selected day
/// default: true
final bool circleSelectedDay;
/// specify the first day of week: DateTime.monday / DateTime.tuesday / ... / DateTime.sunday
/// default: DateTime.sunday
final int firstDayOfWeek;
/// swipe left / right to change the month
/// default: true
final bool swipeToNavigate;
const NBCalendar({
this.titleBuilder,
this.showHeader = true,
this.monthPickerMode = MonthPickerMode.GRID,
this.selectedMonthAndYear,
this.selectedDate,
this.dayEventIndicator = const {},
this.eventIndicatorColor,
this.onMonthChanged,
this.onDateSelected,
this.weekDayLabels = const {
DateTime.monday: 'M',
DateTime.tuesday: 'T',
DateTime.wednesday: 'W',
DateTime.thursday: 'T',
DateTime.friday: 'F',
DateTime.saturday: 'S',
DateTime.sunday: 'S',
},
this.firstDayOfWeek = DateTime.sunday,
this.showInActiveMonthDays = true,
this.circleSelectedDay = true,
this.swipeToNavigate = true,
}) : assert(weekDayLabels != null && weekDayLabels.length == 7,
'There must be configured labels for all 7 days of a week');
@override
State<StatefulWidget> createState() => _NBCalendarState();
}
class _NBCalendarState extends State<NBCalendar> {
/// 'month' view
MonthAndYear _selectedMonthAndYear;
DateTime _selectedDate;
bool _changingMonth = false;
@override
Widget build(BuildContext context) => widget.swipeToNavigate
? Dismissible(
key: UniqueKey(),
confirmDismiss: _onHorizonSwipe,
movementDuration: Duration.zero,
resizeDuration: Duration.zero,
child: _calendar(),
)
: _calendar();
Widget _calendar() => Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Visibility(
visible: widget.showHeader,
replacement: Container(),
child: _CalendarHeader(
monthAndYear: selectedMonthAndYear,
titleBuilder: widget.titleBuilder ?? defaultTitleBuilder,
monthPickerMode: widget.monthPickerMode,
titleStyle: defaultTitleStyle,
onPrevSelected: !_changingMonth ? _onPrev : null,
onNextSelected: !_changingMonth ? _onNext : null,
onMonthChanged: (monthAndYear) {
if (!selectedMonthAndYear.equals(monthAndYear)) {
setState(() {
_selectedMonthAndYear = monthAndYear;
});
if (widget.onMonthChanged != null) {
widget.onMonthChanged(monthAndYear);
}
}
},
),
),
_WeekDays(
weekDayLabels: widget.weekDayLabels,
weekdayLabelStyle: defaultWeekdayLabelStyle,
firstDayOfWeek: widget.firstDayOfWeek,
),
_MonthDays(
monthAndYear: selectedMonthAndYear,
firstDayOfWeek: widget.firstDayOfWeek,
showInActiveMonthDays: widget.showInActiveMonthDays,
monthDayLabelStyle: defaultMonthdayLabelStyle,
inactiveMonthDayLabelStyle: defaultInactiveMonthDayLabelStyle,
selectedDate: selectedDate,
onDateSelected: _onDateSelected,
circleSelectedDay: widget.circleSelectedDay,
dayEventIndicator: widget.dayEventIndicator,
),
],
),
);
MonthAndYear get selectedMonthAndYear =>
_selectedMonthAndYear ??
(widget.selectedMonthAndYear ??
MonthAndYear.fromDateTime(DateTime.now()));
DateTime get selectedDate =>
_selectedDate ?? (widget.selectedDate ?? DateTime.now());
Future<bool> _onHorizonSwipe(DismissDirection direction) async {
if (direction == DismissDirection.startToEnd) {
_onPrev();
} else {
_onNext();
}
return true;
}
void _onPrev() {
setState(() {
_changingMonth = true;
});
final prevMonth = MonthAndYear.fromDateTime(
DateTime(selectedMonthAndYear.year, selectedMonthAndYear.month, -1));
setState(() {
_selectedMonthAndYear = prevMonth;
_changingMonth = false;
});
if (widget.onMonthChanged != null) {
widget.onMonthChanged(prevMonth);
}
}
void _onNext() {
setState(() {
_changingMonth = true;
});
final nextMonth = MonthAndYear.fromDateTime(DateTime(
selectedMonthAndYear.year,
selectedMonthAndYear.month + 1,
));
setState(() {
_selectedMonthAndYear = nextMonth;
_changingMonth = false;
});
if (widget.onMonthChanged != null) {
widget.onMonthChanged(nextMonth);
}
}
void _onDateSelected(DateTime dateTime) {
// don't fire the event multiple time on same date
if (!_isEqualDate(dateTime, _selectedDate)) {
setState(() {
_selectedDate = dateTime;
});
if (widget.onDateSelected != null) {
widget.onDateSelected(dateTime);
}
}
}
}
/// build the calendar title based on input date time. e.g. Jan 2020
typedef String CalendarTitleBuilder(MonthAndYear monthAndYear);
/// when the current month is changed
typedef void MonthChangedCallBack(MonthAndYear monthAndYear);
typedef void OnDateSelected(DateTime dateTime);
final defaultTitleBuilder = (MonthAndYear selectedMonth) =>
DateFormat.yMMM().format(selectedMonth.toDateTime());