Skip to content

Commit

Permalink
✨ Handle daily & weekly reoccurrence events
Browse files Browse the repository at this point in the history
  • Loading branch information
shubham-jitiya-simform committed Oct 25, 2024
1 parent 982747f commit 4933362
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 13 deletions.
2 changes: 2 additions & 0 deletions example/lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'app_colors.dart';
class AppConstants {
AppConstants._();

static final List<String> weekTitles = ["M", "T", "W", "T", "F", "S", "S"];

static OutlineInputBorder inputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(7),
borderSide: BorderSide(
Expand Down
8 changes: 8 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,19 @@ List<CalendarEventData> _events = [
description: "Today is project meeting.",
startTime: DateTime(_now.year, _now.month, _now.day, 18, 30),
endTime: DateTime(_now.year, _now.month, _now.day, 22),
recurrenceSettings: RecurrenceSettings(
_now.add(Duration(days: 1)),
frequency: RepeatFrequency.daily,
),
),
CalendarEventData(
date: _now.add(Duration(days: 1)),
startTime: DateTime(_now.year, _now.month, _now.day, 18),
endTime: DateTime(_now.year, _now.month, _now.day, 19),
recurrenceSettings: RecurrenceSettings(
_now.add(Duration(days: 1)),
frequency: RepeatFrequency.weekly,
),
title: "Wedding anniversary",
description: "Attend uncle's wedding anniversary.",
),
Expand Down
142 changes: 133 additions & 9 deletions example/lib/widgets/add_event_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {

DateTime? _startTime;
DateTime? _endTime;
RepeatFrequency? _selectedFrequency;
List<bool> _selectedDays = List.filled(7, false);

Color _color = Colors.blue;

Expand All @@ -43,6 +45,7 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
super.initState();

_setDefaults();
_setInitialWeekday();
}

@override
Expand Down Expand Up @@ -102,7 +105,9 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
}

_startDate = date.withoutTime;

// Reset weekday of new start date
_selectedDays.fillRange(0, _selectedDays.length, false);
_selectedDays[date.weekday - 1] = true;
if (mounted) {
setState(() {});
}
Expand Down Expand Up @@ -247,6 +252,106 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
hintText: "Event Description",
),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Repeat",
style: TextStyle(
color: AppColors.black,
fontWeight: FontWeight.w500,
fontSize: 17,
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Radio<RepeatFrequency>(
value: RepeatFrequency.doNotRepeat,
groupValue: _selectedFrequency,
onChanged: (value) {
setState(
() => _selectedFrequency = value,
);
},
),
Text(
"Do not repeat",
style: TextStyle(
color: AppColors.black,
fontSize: 17,
),
),
],
),
Row(
children: [
Radio<RepeatFrequency>(
value: RepeatFrequency.daily,
groupValue: _selectedFrequency,
onChanged: (value) {
setState(
() => _selectedFrequency = value,
);
},
),
Text(
"Daily",
style: TextStyle(
color: AppColors.black,
fontSize: 17,
),
)
],
),
Row(
children: [
Radio<RepeatFrequency>(
value: RepeatFrequency.weekly,
groupValue: _selectedFrequency,
onChanged: (value) {
setState(
() => _selectedFrequency = value,
);
},
),
Text(
"Weekly",
style: TextStyle(
color: AppColors.black,
fontSize: 17,
),
),
],
)
],
),
if (_selectedFrequency == RepeatFrequency.weekly) ...[
Wrap(
children: List<Widget>.generate(AppConstants.weekTitles.length,
(int index) {
return ChoiceChip(
label: Text(AppConstants.weekTitles[index]),
showCheckmark: false,
selected: _selectedDays[index],
onSelected: (bool selected) {
setState(() {
_selectedDays[index] = selected;
if (!_selectedDays.contains(true)) {
_selectedDays[_startDate.weekday - 1] = true;
}
});

debugPrint('weekdays: ${_selectedDays}');
debugPrint('weekdays: ${_selectedIndexes}');
},
shape: CircleBorder(),
);
}).toList(),
),
],
SizedBox(
height: 15.0,
),
Expand Down Expand Up @@ -286,19 +391,38 @@ class _AddOrEditEventFormState extends State<AddOrEditEventForm> {
_form.currentState?.save();

final event = CalendarEventData(
date: _startDate,
endTime: _endTime,
startTime: _startTime,
endDate: _endDate,
color: _color,
title: _titleController.text.trim(),
description: _descriptionController.text.trim(),
);
date: _startDate,
endTime: _endTime,
startTime: _startTime,
endDate: _endDate,
color: _color,
title: _titleController.text.trim(),
description: _descriptionController.text.trim(),
recurrenceSettings: RecurrenceSettings(
_startDate,
frequency: _selectedFrequency ?? RepeatFrequency.daily,
weekdays: _selectedIndexes,
));

widget.onEventAdd?.call(event);
_resetForm();
}

List<int> get _selectedIndexes {
List<int> selectedIndexes = [];
for (int i = 0; i < _selectedDays.length; i++) {
if (_selectedDays[i] == true) {
selectedIndexes.add(i);
}
}
return selectedIndexes;
}

void _setInitialWeekday() {
final currentWeekday = DateTime.now().weekday - 1;
_selectedDays[currentWeekday] = true;
}

void _setDefaults() {
if (widget.event == null) return;

Expand Down
5 changes: 5 additions & 0 deletions lib/src/calendar_event_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class CalendarEventData<T extends Object?> {
/// Define style of description.
final TextStyle? descriptionStyle;

/// Define reoccurrence settings
final RecurrenceSettings? recurrenceSettings;

/// {@macro calendar_event_data_doc}
CalendarEventData({
required this.title,
Expand All @@ -55,6 +58,7 @@ class CalendarEventData<T extends Object?> {
this.endTime,
this.titleStyle,
this.descriptionStyle,
this.recurrenceSettings,
DateTime? endDate,
}) : _endDate = endDate?.withoutTime,
date = date.withoutTime;
Expand Down Expand Up @@ -119,6 +123,7 @@ class CalendarEventData<T extends Object?> {
"title": title,
"description": description,
"endDate": endDate,
"recurrenceSettings": recurrenceSettings,
};

/// Returns new object of [CalendarEventData] with the updated values defined
Expand Down
9 changes: 9 additions & 0 deletions lib/src/enumerations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@ enum LineStyle {
/// Dashed line
dashed,
}

/// Defines reoccurrence of event: Daily, weekly, monthly or yearly
enum RepeatFrequency {
doNotRepeat,
daily,
weekly,
monthly,
yearly,
}
45 changes: 41 additions & 4 deletions lib/src/event_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:collection';
import 'package:flutter/material.dart';

import 'calendar_event_data.dart';
import 'enumerations.dart';
import 'extensions.dart';
import 'typedefs.dart';

Expand Down Expand Up @@ -137,9 +138,43 @@ class EventController<T extends Object?> extends ChangeNotifier {
{bool includeFullDayEvents = true}) {
//ignore: deprecated_member_use_from_same_package
if (_eventFilter != null) return _eventFilter!.call(date, this.events);

return _calendarData.getEventsOnDay(date.withoutTime,
final events = _calendarData.getEventsOnDay(date.withoutTime,
includeFullDayEvents: includeFullDayEvents);
events.addAll(getRepeatedEvents(date));
return events;
}

/// Filters list of repeated events to show in the cell for given date
/// from all the repeated events.
/// Event reoccurrence will only show after today's date and event's day.
List<CalendarEventData<T>> getRepeatedEvents(DateTime date) {
if (!date.isAfter(DateTime.now())) {
return [];
}
final repeatedEvents = _calendarData.repeatedEvents;
List<CalendarEventData<T>> events = [];
for (final event in repeatedEvents) {
switch (event.recurrenceSettings!.frequency) {
case RepeatFrequency.daily:
events.add(event);
break;
case RepeatFrequency.weekly:
if (event.recurrenceSettings!.weekdays.contains(date.weekday - 1)) {
events.add(event);
}
break;
case RepeatFrequency.monthly:
// TODO: Handle this case.
break;
case RepeatFrequency.yearly:
// TODO: Handle this case.
break;
case RepeatFrequency.doNotRepeat:
// TODO: Handle this case.
break;
}
}
return events;
}

/// Returns full day events on given day.
Expand Down Expand Up @@ -179,6 +214,10 @@ class CalendarData<T extends Object?> {
/// available in this list as global itemList of all events).
final _eventList = <CalendarEventData<T>>[];

/// If recurrence settings exist then get all the repeated events
List<CalendarEventData<T>> get repeatedEvents =>
_eventList.where((event) => event.recurrenceSettings != null).toList();

UnmodifiableListView<CalendarEventData<T>> get events =>
UnmodifiableListView(_eventList);

Expand Down Expand Up @@ -249,7 +288,6 @@ class CalendarData<T extends Object?> {

// TODO: improve this...
if (_eventList.contains(event)) return;

if (event.isFullDayEvent) {
addFullDayEvent(event);
} else if (event.isRangingEvent) {
Expand Down Expand Up @@ -329,7 +367,6 @@ class CalendarData<T extends Object?> {
if (includeFullDayEvents) {
events.addAll(getFullDayEvent(date));
}

return events;
}

Expand Down
25 changes: 25 additions & 0 deletions lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ extension DateTimeExtensions on DateTime {
"This extension is not being used in this package and will be removed "
"in next major release. Please use withoutTime instead.")
DateTime get dateYMD => DateTime(year, month, day);

/// Get weekday from day of date
WeekDays get getWeekDays {
switch (this.weekday) {
case 1:
return WeekDays.monday;
case 2:
return WeekDays.tuesday;
case 3:
return WeekDays.wednesday;
case 4:
return WeekDays.thursday;
case 5:
return WeekDays.friday;
case 6:
return WeekDays.saturday;
case 7:
return WeekDays.sunday;
default:
throw Exception("""
Date $this has unrecognisable weekday expencted [1-7],
but ${this.weekday} was provided.
""");
}
}
}

extension ColorExtension on Color {
Expand Down
33 changes: 33 additions & 0 deletions lib/src/modals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,36 @@ class LiveTimeIndicatorSettings {
showBullet: false,
);
}

/// Set `frequency = RepeatFrequency.daily` to repeat every day after current date & event day.
/// Set `frequency = RepeatFrequency.weekly` & provide list of weekdays to repeat on.
/// [startDate]: Defines start date of repeating events.
/// [endDate]: Defines end date of repeating events.
/// [interval]: Defines repetition of event after given [interval] in days.
/// [frequency]: Defines repeat daily, weekly, monthly or yearly.
/// [weekdays]: Contains list of weekdays to repeat on.
/// By default weekday of event is considered if not provided.
class RecurrenceSettings {
final DateTime startDate;
final DateTime? endDate;
final int? interval;
final RepeatFrequency frequency;
final List<int> weekdays;

RecurrenceSettings(
this.startDate, {
this.endDate,
this.interval,
this.frequency = RepeatFrequency.weekly,
List<int>? weekdays,
}) : weekdays = weekdays ?? [startDate.weekday];

@override
String toString() {
return "start date: ${startDate}, "
"end date: ${endDate}, "
"interval: ${interval}, "
"frequency: ${frequency} "
"weekdays: ${weekdays.toString()}";
}
}
Loading

0 comments on commit 4933362

Please sign in to comment.