Skip to content

Commit 246669a

Browse files
rashi-simformPRBaraiya
authored andcommitted
feat: ✨Enhance event arrangement logic to support multi-day events
- Resolved issue of event not getting added till midnight
1 parent bfc8922 commit 246669a

File tree

8 files changed

+621
-390
lines changed

8 files changed

+621
-390
lines changed

example/lib/widgets/add_event_form.dart

Lines changed: 373 additions & 282 deletions
Large diffs are not rendered by default.

example/lib/widgets/date_time_selector.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ class _DateTimeSelectorFormFieldState extends State<DateTimeSelectorFormField> {
7575
_textEditingController = widget.controller ?? TextEditingController();
7676
}
7777

78-
if (_selectedDate != oldWidget.initialDateTime ||
78+
if (widget.initialDateTime != oldWidget.initialDateTime ||
79+
_selectedDate != oldWidget.initialDateTime ||
7980
widget.minimumDateTime != oldWidget.minimumDateTime) {
8081
_setDates();
8182
}
@@ -104,7 +105,7 @@ class _DateTimeSelectorFormFieldState extends State<DateTimeSelectorFormField> {
104105
@override
105106
Widget build(BuildContext context) {
106107
return GestureDetector(
107-
onTap: _showSelector,
108+
onTap: widget.onSelect == null ? null : _showSelector,
108109
child: TextFormField(
109110
focusNode: _focusNode,
110111
style: widget.textStyle,

lib/src/components/_internal_components.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,13 @@ class EventGenerator<T extends Object?> extends StatelessWidget {
385385
/// of events and [eventTileBuilder] to display events.
386386
List<Widget> _generateEvents(BuildContext context) {
387387
final events = eventArranger.arrange(
388-
events: this.events,
389-
height: height,
390-
width: width,
391-
heightPerMinute: heightPerMinute,
392-
startHour: startHour);
393-
388+
events: this.events,
389+
height: height,
390+
width: width,
391+
heightPerMinute: heightPerMinute,
392+
startHour: startHour,
393+
calendarViewDate: date,
394+
);
394395
return List.generate(events.length, (index) {
395396
return Positioned(
396397
top: events[index].top,

lib/src/event_arrangers/event_arrangers.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ abstract class EventArranger<T extends Object?> {
3737
required double width,
3838
required double heightPerMinute,
3939
required int startHour,
40+
required DateTime calendarViewDate,
4041
});
4142
}
4243

@@ -63,6 +64,9 @@ class OrganizedCalendarEventData<T extends Object?> {
6364
/// End duration of event/event list.
6465
final DateTime endDuration;
6566

67+
/// DateTime of the calendar view date.
68+
final DateTime calendarViewDate;
69+
6670
/// Provides event data with its [left], [right], [top], and [bottom]
6771
/// boundary.
6872
OrganizedCalendarEventData({
@@ -73,6 +77,7 @@ class OrganizedCalendarEventData<T extends Object?> {
7377
required this.left,
7478
required this.right,
7579
required this.events,
80+
required this.calendarViewDate,
7681
});
7782

7883
OrganizedCalendarEventData.empty()
@@ -82,7 +87,8 @@ class OrganizedCalendarEventData<T extends Object?> {
8287
left = 0,
8388
events = const [],
8489
top = 0,
85-
bottom = 0;
90+
bottom = 0,
91+
calendarViewDate = DateTime.now();
8692

8793
OrganizedCalendarEventData<T> getWithUpdatedRight(double right) =>
8894
OrganizedCalendarEventData<T>(
@@ -93,5 +99,6 @@ class OrganizedCalendarEventData<T extends Object?> {
9399
left: left,
94100
right: right,
95101
startDuration: startDuration,
102+
calendarViewDate: calendarViewDate,
96103
);
97104
}

lib/src/event_arrangers/merge_event_arranger.dart

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
3232
required double width,
3333
required double heightPerMinute,
3434
required int startHour,
35+
required DateTime calendarViewDate,
3536
}) {
3637
// TODO: Right now all the events that are passed in this function must be
3738
// sorted in ascending order of the start time.
3839
//
3940
final arrangedEvents = <OrganizedCalendarEventData<T>>[];
41+
final startHourInMinutes = startHour * 60;
4042

4143
//Checking if startTime and endTime are correct
4244
for (final event in events) {
@@ -46,7 +48,8 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
4648
}
4749

4850
// Checks if an event has valid start and end time.
49-
if (event.endTime!.getTotalMinutes <= event.startTime!.getTotalMinutes) {
51+
if (event.endDate.millisecondsSinceEpoch <
52+
event.date.millisecondsSinceEpoch) {
5053
if (!(event.endTime!.getTotalMinutes == 0 &&
5154
event.startTime!.getTotalMinutes > 0)) {
5255
assert(() {
@@ -67,13 +70,51 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
6770
final startTime = event.startTime!;
6871
final endTime = event.endTime!;
6972

70-
// startTime.getTotalMinutes returns the number of minutes from 00h00 to the beginning of the event
71-
// But the first hour to be displayed (startHour) could be 06h00, so we have to substract
72-
// The number of minutes from 00h00 to startHour which is equal to startHour * 60
73-
final eventStart = startTime.getTotalMinutes - (startHour * 60);
74-
final eventEnd = endTime.getTotalMinutes - (startHour * 60) == 0
75-
? Constants.minutesADay - (startHour * 60)
76-
: endTime.getTotalMinutes - (startHour * 60);
73+
int eventStart;
74+
int eventEnd;
75+
76+
if (event.isRangingEvent) {
77+
// Handle multi-day events differently based on which day is currently being viewed
78+
final isStartDate =
79+
calendarViewDate.isAtSameMomentAs(event.date.withoutTime);
80+
final isEndDate =
81+
calendarViewDate.isAtSameMomentAs(event.endDate.withoutTime);
82+
83+
if (isStartDate && isEndDate) {
84+
// Single day event with start and end time
85+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
86+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
87+
? Constants.minutesADay - (startHourInMinutes)
88+
: endTime.getTotalMinutes - (startHourInMinutes);
89+
} else if (isStartDate) {
90+
// First day - show from start time to end of day
91+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
92+
eventEnd = Constants.minutesADay;
93+
} else if (isEndDate) {
94+
// Last day - show from start of day to end time
95+
eventStart = 0;
96+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
97+
? Constants.minutesADay - (startHourInMinutes)
98+
: endTime.getTotalMinutes - (startHourInMinutes);
99+
} else {
100+
// Middle days - show full day
101+
eventStart = 0;
102+
eventEnd = Constants.minutesADay;
103+
}
104+
} else {
105+
// Single day event - use normal start/end times
106+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
107+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
108+
? Constants.minutesADay - (startHourInMinutes)
109+
: endTime.getTotalMinutes - (startHourInMinutes);
110+
}
111+
112+
// Ensure values are within valid range
113+
eventStart = math.max(0, eventStart);
114+
eventEnd = math.min(
115+
Constants.minutesADay - (startHourInMinutes),
116+
eventEnd,
117+
);
77118

78119
final arrangeEventLen = arrangedEvents.length;
79120

@@ -97,8 +138,13 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
97138

98139
if (eventIndex == -1) {
99140
final top = eventStart * heightPerMinute;
100-
final bottom = eventEnd * heightPerMinute == height
101-
? 0.0
141+
142+
// Calculate visibleMinutes (the total minutes displayed in the view)
143+
final visibleMinutes = Constants.minutesADay - (startHourInMinutes);
144+
145+
// Check if event ends at or beyond the visible area
146+
final bottom = eventEnd >= visibleMinutes
147+
? 0.0 // Event extends to bottom of view
102148
: height - eventEnd * heightPerMinute;
103149

104150
final newEvent = OrganizedCalendarEventData<T>(
@@ -109,6 +155,7 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
109155
startDuration: startTime.copyFromMinutes(eventStart),
110156
endDuration: endTime.copyFromMinutes(eventEnd),
111157
events: [event],
158+
calendarViewDate: calendarViewDate,
112159
);
113160

114161
arrangedEvents.add(newEvent);
@@ -126,8 +173,13 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
126173
final endDuration = math.max(eventEnd, arrangedEventEnd);
127174

128175
final top = startDuration * heightPerMinute;
129-
final bottom = endDuration * heightPerMinute == height
130-
? 0.0
176+
177+
// Calculate visibleMinutes (the total minutes displayed in the view)
178+
final visibleMinutes = Constants.minutesADay - (startHourInMinutes);
179+
180+
// Check if event ends at or beyond the visible area
181+
final bottom = endDuration >= visibleMinutes
182+
? 0.0 // Event extends to bottom of view
131183
: height - endDuration * heightPerMinute;
132184

133185
final newEvent = OrganizedCalendarEventData<T>(
@@ -140,6 +192,7 @@ class MergeEventArranger<T extends Object?> extends EventArranger<T> {
140192
endDuration:
141193
arrangedEventData.endDuration.copyFromMinutes(endDuration),
142194
events: arrangedEventData.events..add(event),
195+
calendarViewDate: calendarViewDate,
143196
);
144197

145198
arrangedEvents[eventIndex] = newEvent;

lib/src/event_arrangers/side_event_arranger.dart

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ class SideEventArranger<T extends Object?> extends EventArranger<T> {
3838
required double width,
3939
required double heightPerMinute,
4040
required int startHour,
41+
required DateTime calendarViewDate,
4142
}) {
4243
final totalWidth = width;
44+
final startHourInMinutes = startHour * 60;
4345

4446
List<_SideEventConfigs<T>> _categorizedColumnedEvents(
4547
List<CalendarEventData<T>> events) {
@@ -49,6 +51,7 @@ class SideEventArranger<T extends Object?> extends EventArranger<T> {
4951
width: width,
5052
heightPerMinute: heightPerMinute,
5153
startHour: startHour,
54+
calendarViewDate: calendarViewDate,
5255
);
5356

5457
final arranged = <_SideEventConfigs<T>>[];
@@ -117,27 +120,71 @@ class SideEventArranger<T extends Object?> extends EventArranger<T> {
117120
final startTime = e.startTime!;
118121
final endTime = e.endTime!;
119122

120-
// startTime.getTotalMinutes returns the number of minutes from 00h00 to the beginning hour of the event
121-
// But the first hour to be displayed (startHour) could be 06h00, so we have to substract
122-
// The number of minutes from 00h00 to startHour which is equal to startHour * 60
123+
int eventStart;
124+
int eventEnd;
125+
126+
if (e.isRangingEvent) {
127+
// Handle multi-day events differently based on which day is currently being viewed
128+
final isStartDate =
129+
calendarViewDate.isAtSameMomentAs(e.date.withoutTime);
130+
final isEndDate =
131+
calendarViewDate.isAtSameMomentAs(e.endDate.withoutTime);
132+
133+
if (isStartDate && isEndDate) {
134+
// Single day event with start and end time
135+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
136+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
137+
? Constants.minutesADay - (startHourInMinutes)
138+
: endTime.getTotalMinutes - (startHourInMinutes);
139+
} else if (isStartDate) {
140+
// First day - show from start time to end of day
141+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
142+
eventEnd = Constants.minutesADay - (startHourInMinutes);
143+
} else if (isEndDate) {
144+
// Last day - show from start of day to end time
145+
eventStart = 0;
146+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
147+
? Constants.minutesADay - (startHourInMinutes)
148+
: endTime.getTotalMinutes - (startHourInMinutes);
149+
} else {
150+
// Middle days - show full day
151+
eventStart = 0;
152+
eventEnd = Constants.minutesADay - (startHourInMinutes);
153+
}
154+
} else {
155+
// Single day event - use normal start/end times
156+
eventStart = startTime.getTotalMinutes - (startHourInMinutes);
157+
eventEnd = endTime.getTotalMinutes - (startHourInMinutes) <= 0
158+
? Constants.minutesADay - (startHourInMinutes)
159+
: endTime.getTotalMinutes - (startHourInMinutes);
160+
}
161+
162+
// Ensure values are within valid range
163+
eventStart = math.max(0, eventStart);
164+
eventEnd = math.min(
165+
Constants.minutesADay - (startHourInMinutes),
166+
eventEnd,
167+
);
168+
169+
final top = eventStart * heightPerMinute;
123170

124-
final bottom = height -
125-
(endTime.getTotalMinutes - (startHour * 60) == 0
126-
? Constants.minutesADay - (startHour * 60)
127-
: endTime.getTotalMinutes - (startHour * 60)) *
128-
heightPerMinute;
171+
// Calculate visibleMinutes (the total minutes displayed in the view)
172+
final visibleMinutes = Constants.minutesADay - (startHourInMinutes);
129173

130-
final top = (startTime.getTotalMinutes - (startHour * 60)) *
131-
heightPerMinute;
174+
// Check if event ends at or beyond the visible area
175+
final bottom = eventEnd >= visibleMinutes
176+
? 0.0 // Event extends to bottom of view
177+
: height - eventEnd * heightPerMinute;
132178

133179
return OrganizedCalendarEventData<T>(
134180
left: offset,
135181
right: totalWidth - (offset + slotWidth),
136182
top: top,
137183
bottom: bottom,
138-
startDuration: startTime,
139-
endDuration: endTime,
184+
startDuration: startTime.copyFromMinutes(eventStart),
185+
endDuration: endTime.copyFromMinutes(eventEnd),
140186
events: [e],
187+
calendarViewDate: calendarViewDate,
141188
);
142189
}));
143190
}

lib/src/event_controller.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,13 @@ class CalendarData<T extends Object?> {
570570
.where((event) => (!event.isFullDayEvent && !event.isRangingEvent))
571571
.toList();
572572
events.addAll(recurringEvents);
573+
574+
// Inside event arranger we require all the events to be sorted
575+
// based on start time.
576+
//If not sorted it will create overlapping events in the arranger.
577+
events.sort((a, b) =>
578+
(a.startTime?.getTotalMinutes ?? 0) -
579+
(b.startTime?.getTotalMinutes ?? 0));
573580
return events;
574581
}
575582

0 commit comments

Comments
 (0)