I am working on a Flutter project where I am displaying a calendar of items on the screen. The screen shows a week’s worth of entries and each day was represented by a stateful widget that queried the application server for entries belonging to its day.
When the user adds, edits or deletes an entry, I want to be able to update the display, but I only want the days that are affected by change to reload. I use Provider as my state management library so I added a ChangeNotifier as a parent to the calendar and whenever a change happens it notifies the children which then update when an add, edit or delete happen.
When a new entry is created typically only one day of the seven on screen needs to reload. When an entry is deleted, it is the same, typically only one day needs to reload. When an entry is updated, it is common that one day will need to reload, but if an entry is modified so that it is moved from one day to another (e.g. it is moved from Friday to Thursday) then there are two days affected by the change.
Each entry on the calendar is represented by a start and end time. When an entry is deleted or added, I add a DateTimeRange to the ChangeNotifier which represents the time frame of the calendar item. When an item is updated and moved to a new time, then I add a List of DateTimeRange to the ChangeNotifier, the first DateTimeRange representing the initial range and the second value represents where the item got moved to.
The challenge is then on the child widgets, the individual days, that are listening for these changes. When a change comes, it needs to figure out if the day is affected by the change, and if yes, then reload the data. To do that, I wrote a function called “calcOverlap“. This function returns a Duration if there is an overlap and a null if there is no overlap.
In the case of my project, it loops through all the DateTimeRange values and if any have overlap with the current day (which itself is a DateTimeRange) then it reloads the data.
The following code is a demonstration of the “calcOverlap” function and you can test it out at dart.dev:
class DateTimeRange {
DateTime start;
DateTime end;
DateTimeRange({required this.start, required this.end}) {
assert(start.isBefore(end));
}
}
Duration? calcOverlap(DateTimeRange r1, DateTimeRange r2) {
DateTime start = r1.start;
if (r2.start.isAfter(start)) {
start = r2.start;
}
DateTime end = r1.end;
if (r2.end.isBefore(end)) {
end = r2.end;
}
if (end.isBefore(start)) {
return null;
} else {
return end.difference(start);
}
}
void main() {
DateTimeRange r1 = DateTimeRange(
start: DateTime(2023, 1, 1, 13, 0),
end: DateTime(2023, 1, 1, 14, 15),
);
DateTimeRange r2 = DateTimeRange(
start: DateTime(2023, 1, 2, 13, 0),
end: DateTime(2023, 1, 2, 14, 15),
);
DateTimeRange r3 = DateTimeRange(
start: DateTime(2023, 1, 1, 13, 30),
end: DateTime(2023, 1, 2, 14, 00),
);
print("Overlap between r1 and r2: ${calcOverlap(r1, r2)}");
print("Overlap between r1 and r3: ${calcOverlap(r1, r3)}");
print("Overlap between r2 and r3: ${calcOverlap(r2, r3)}");
print("Overlap between r2 and r2: ${calcOverlap(r2, r2)}");
}
The results will be:
Overlap between r1 and r2: null
Overlap between r1 and r3: 0:45:00.000000
Overlap between r2 and r3: 1:00:00.000000
Overlap between r2 and r2: 1:15:00.000000