NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #2690 most downloaded on pub.dev
iOS implementation of the device_calendar_plus plugin.
Last release 6 days ago
02 Oct 2026
Ships fairly regularly
a new release about every 6 weeks
Nearly every release is documented
notes for 19 of 19 stable releases
Nothing withdrawn
no release was ever pulled
11 months old
19 releases · first in 2025
One column per month.
updateRecurring refuses a start move with no new rule unless the existing rule generates the series' new start. It compared only the weekday, day of m
updateRecurring refuses a start move with no new rule unless the existing
rule generates the series' new start. It compared only the weekday, day of
month and month the rule pins, so an ordinal BYDAY or a BYSETPOS series
(the 4th Thursday of November, say) could move to another occurrence of its
weekday (the 3rd Thursday), leaving the start on a day the rule never
generates (#189). A move onto another day the rule generates (Monday to
Wednesday of a Mon/Wed/Fri rule) is now allowed through allEvents; with
thisAndFollowing a move to another day still throws while the rule pins
days, until iOS can split such a series (#194).updateRecurring on an all-day series adds duration (or keeps the
existing span, when only start moves) in calendar days rather than
86,400-second days, so a multi-day all-day series no longer loses or gains
its last day when its span crosses a DST change (#195).updateEvent and updateRecurring write an all-day event's end as the
last second of its last day, the way EventKit stores it. An exclusive
midnight end written onto an existing all-day event read back as one more
day, so a two-day duration or end gave three (#195).Minimum supported SDK is now Flutter 3.44 / Dart 3.12. Android migrated to Flutter's built-in Kotlin, so the KGP deprecation warning no longer prints…
flutter build (#133).Recurring events
updateRecurring with a new rule anchors the series on the first day thatinvalidArguments and the series is left untouched (#140).updateEvent or deleteEvent on a single occurrence of adeleteRecurring with thisAndFollowing also removes anupdateRecurring with thisAndFollowing carries an occurrence ongetEvent resolves all-day recurring instance IDs (it alwaysSynced calendars
Listing events
listEvents returns an all-day event when the window is a sub-daylistEvents rejects an endDate before startDate with ArgumentError,endDate == startDate) with no events on both platforms (#162).Calendars
updateCalendar and deleteCalendar throw the documented readOnly for aoperationFailed; Android renamed or deleted any row itcreateCalendar refuses a non-local accountType with readOnly,listSources already reports and iOS already does for non-creatablecreateCalendar / updateCalendar throw ArgumentError for a colorHex#RRGGBB (the # optional) instead of storing it silently as#RRGGBB to the platform (#126).deleteCalendar('') throws ArgumentError, like the other mutations (#126).listCalendars no longer crashes on a provider row with a NULLCalendarSource.supportsCalendarCreation, CreateCalendarOptionsIos andCreateCalendarOptionsAndroid describe what the code actually does: iOSPlatform packages: device_calendar_plus_android 0.8.0, device_calendar_plus_ios 0.8.0.
updateEvent and deleteEvent without a timestamp refuse
a recurring series with INVALID_ARGUMENTS and write nothing; series-wide
changes go through updateRecurring / deleteRecurring (#175).updateRecurring forwards a reminders patch. A thisAndFollowing split
that clears the rule keeps the occurrence's reminders on the standalone
event it leaves, as Android does (#175).updateRecurring(allEvents) switching a timed series to all-day shifts the
start in local time, where EventKit puts all-day events. It shifted in the
series' stored zone, so a UTC-stored series landed on UTC midnight, which
west of UTC is the day before: the series gained an extra first occurrence
off its BYDAY and lost its last one. The day-move check reads each date in
its own zone too (#187).updateRecurring and deleteRecurring with thisAndFollowing refuse a
non-recurring event with INVALID_ARGUMENTS and write nothing, as Android
does. A timestamp that fell inside the event matched it, so the whole event
was edited or deleted and the call reported success (#124).updateRecurring refuses a start move with no new rule when it breaks any
day the rule pins, as Android does. It checked only BYDAY when the rule
had one, so a yearly BYMONTH=11;BYDAY=4TH series could move to a
Thursday in December (#188).showEventModal/showCreateEventModal
while one is showing fails OPERATION_FAILED instead of orphaning the
first reply; swiping the view modal down resolves it; a modal is presented
from the top of the presentation stack, so an app sheet that's already up
no longer swallows it; and a missing root view controller fails
OPERATION_FAILED instead of crashing with fatalError.createEvent refuses a recurrence rule it can't parse (a FREQ other than
DAILY/WEEKLY/MONTHLY/YEARLY, or malformed input) with
INVALID_ARGUMENTS and writes nothing, as updateRecurring already does.
It used to drop the rule and save a one-off event (#125).writeOnly rather than notDetermined, so a createEvent fired
straight after the prompt passes its permission gate. The request now waits
briefly for the OS status to catch up with an ungranted answer (#137).Per-event color lands, plus one rename to swallow.
Per-event color lands, plus one rename to swallow.
Event.colorHex — the event's custom color as #RRGGBB, with a parsed Event.color getter (read-only, mirrors Calendar.colorHex). Android reads it from EVENT_COLOR; null when the event just uses its calendar's color, and always null on iOS (EventKit has no per-event colors). No write support (#117).WeeklyRecurrence's wkst field and constructor parameter are renamed to weekStart, matching the spelled-out naming of the other recurrence fields. The emitted RRULE is unchanged (still WKST=). Update WeeklyRecurrence(wkst: …) call sites and .wkst reads to weekStart.Ships device_calendar_plus 0.8.0 and device_calendar_plus_android 0.7.1. The platform interface and iOS packages are unchanged.
updateRecurring with a new rule anchors the series on the first day that
rule generates. Changing a weekly series to another weekday left its start
on the old day, so the first occurrence was stranded there. A rule that
generates no occurrence at all is refused with INVALID_ARGUMENTS and the
series is left untouched (#140).deleteCalendar refuses a calendar EventKit marks immutable or read-only
with READ_ONLY before asking the store, instead of surfacing the refused
remove as OPERATION_FAILED. updateCalendar now checks isImmutable
too, EventKit's own flag for "can't be edited or deleted" (#126).Docs + packaging polish. No code or behaviour changes.
Docs + packaging polish. No code or behaviour changes.
device pub.dev topic (dropped federated to stay within the five-topic limit).doc/.EKEventStore.authorizationStatus still reports
.notDetermined. On iOS 17+ the live status can lag the request handler, so
the first call after a fresh grant failed its permission gate until the app
restarted. The tier the OS confirmed is now remembered for the process and
consulted only when it outranks a stale live status; a terminal denied /
restricted always wins, so a Settings revocation is never masked (#134).showCreateEventModal is gated only below iOS 17, where the editor runs
in-process. On iOS 17+ EKEventEditViewController is out-of-process and
needs no calendar access (#121).NSCalendarsFullAccessUsageDescription
(what the OS demands) rather than the legacy key, and only when a prompt will
actually fire — already-granted and terminal states get their status back
without a configuration error (#121).createEvent with a named calendarId under write-only access reports
permissionDenied instead of notFound (#121).CalendarAuthorization seam:
CalendarAccess normalises EventKit's status across the iOS 17 divide and
PermissionService holds policy only. Swift unit tests cover the grant,
revocation and configuration paths (#134).Feature release. App-facing API is additive; device_calendar_plus_platform_interface has breaking signature changes for platform implementers.
Feature release. App-facing API is additive; device_calendar_plus_platform_interface has breaking signature changes for platform implementers.
requestPermissions(level: CalendarAccessLevel.writeOnly) for the gentler add-only prompt (#89).DeviceCalendar.instance.autoPermissions = AutoPermissionMode.asNeeded | .full; methods request access on first use (#90).calendarId — omit it on createEvent to write to the default calendar (#88).reminders: List<Duration> on create, Patch<List<Duration>> on update, Event.reminders read back; minute-granular relative offsets (#87).permissionDenied (not "no writable calendar") when a default can't be resolved (#112); tighter denial reporting (#108).All four packages bumped 0.6.0 → 0.7.0 in lockstep.
requestWriteOnlyAccessToEvents (iOS 17+). It is not a
permanent ceiling — a later full request re-prompts and upgrades the app
in-app (#89).createEvent with no calendarId writes to defaultCalendarForNewEvents
(#88).EKAlarm(relativeOffset:) on EKEvent.alarms (#87).chore(release): prepare 0.6.0
chore(release): prepare 0.6.0
updateRecurring takes the anchored occurrence's new start
(newStartMillis) instead of startMinuteOfDay. shiftStart translates the
series anchor by the wall-clock delta (calendar-day count + time-of-day) in
the event's EKEvent.timeZone, so a move can change the day and time
together and stays correct across DST (#103).updateRecurring rejects a start that moves the day of a series whose
EKRecurrenceRule pins it (daysOfTheWeek / daysOfTheMonth /
monthsOfTheYear) unless a new rule is also supplied
(dayMoveConflictsWithRule).EventKit reads and writes now run on a background serial queue instead of the main thread, so operations on large calendars no longer stall the UI
listEvents over a span longer than EventKit's ~4-year predicate limit now
chunks the query into windows and de-duplicates recurring instances across
window boundaries, so long ranges return every event exactly once, sorted
(#94)createCalendar now fails with a clear error on sources that can't hold
calendars (e.g. subscribed/holiday sources) instead of an opaque EventKit
failure (#96)updateRecurring(thisAndFollowing, recurrenceRule: Patch.clear()) now splits
the series — truncating the master before the occurrence and detaching a
standalone non-recurring event at the split point — instead of collapsing the
whole series into a single event. Matches Android's behavior (#93)iOS: fix showEventModal(edit: true) crash
showEventModal(edit: true) no longer crashes with NSInvalidArgumentException
("Pushing a navigation controller is not supported"). EKEventEditViewController
is a UINavigationController subclass and is now presented directly instead of
being wrapped in another navigation controller (#77)Breaking: recurring-edit split — updateRecurring / deleteRecurring accept only allEvents and thisAndFollowing (both EKSpan.futureEvents); single occur
updateRecurring / deleteRecurring
accept only allEvents and thisAndFollowing (both EKSpan.futureEvents);
single occurrences go through updateEvent / deleteEvent with an
occurrence timestamp (EKSpan.thisEvent)updateRecurring time changes preserve each occurrence's date, replacing
only the time-of-day componentsEKEvent; an edit whose
startDate passes the occurrence's untouched end now fails with
invalidArguments (matching Android) instead of saving an inverted eventupdateRecurring() — series-level recurring-event edits with EventSpan (allEvents / thisAndFollowing / thisInstance), backed by EKSpan.futureEvents and
updateRecurring() — series-level recurring-event edits with EventSpan (allEvents / thisAndFollowing / thisInstance), backed by EKSpan.futureEvents and EKSpan.thisEventdeleteRecurring() — same EventSpan semantics on EKEventStore.removeurl field on events via EKEvent.urlPatch<T> support in updateEvent() — null leaves a field unchanged, Patch.set writes, Patch.clear nils the field on the EKEventedit flag on showEvent() — presents EKEventEditViewController instead of EKEventViewControllerCalendar source lookup fallback when default source is unavailable
createCalendar default fallback prefers iCloud over other CalDAV sources (#33)Swift Package Manager support (CocoaPods continues to work as before)
Fixed parsing of instanceId for events with @ in their event ID (e.g., Google Calendar IDs like abc123@google.com)
instanceId for events with @ in their event ID (e.g., Google Calendar IDs like abc123@google.com)feat(android): add CreateCalendarOptionsAndroid for custom account na…
feat(android): add CreateCalendarOptionsAndroid for custom account na…
createCalendar() signature updated to accept optional platformOptions parameter (ignored on iOS)showEvent() now properly stores result callback and calls it in eventViewController(_:didCompleteWith:) delegate method after modal is dismissed
showEvent() now properly stores result callback and calls it in eventViewController(_:didCompleteWith:) delegate method after modal is dismissedchore: bump version and update changelogs
chore: bump version and update changelogs
deleteEvent() now always deletes entire series for recurring events using EKSpan.futureEvents on master event (removed deleteAllInstances parameter)updateEvent() now always updates entire series for recurring events using EKSpan.futureEvents on master event (removed updateAllInstances parameter)NOT_SUPPORTED error code (no longer needed)openAppSettings() implementation using UIApplication.openSettingsURLString
openAppSettings() implementation using UIApplication.openSettingsURLStringgetPlatformVersion() implementation (unused boilerplate)Version sync with other packages. No functional changes.
Version sync with other packages. No functional changes.
Initial release.
Initial release.
Your coding agent can read these notes before it upgrades. Set up the MCP server →