Flutter Native Calendar
A Flutter plugin to add events to native calendar on Android and iOS with rich platform-specific settings and features.
Features
- ✅ Open native calendar app with pre-filled event details
- ✅ Add events directly to calendar (with permissions)
- ✅ Request and check calendar permissions
- ✅ Platform-specific settings for Android and iOS
- ✅ Support for reminders, alarms, and advanced calendar features
- ✅ All-day events support
- ✅ Timezone support
- ✅ Structured location support with GPS coordinates and addresses
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
flutter_native_calendar: ^0.3.0
Then run:
flutter pub get
Platform Setup
Android Setup
1. Add Permissions
Add the following permissions to your android/app/src/main/AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Calendar permissions -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<application>
<!-- Your app configuration -->
</application>
</manifest>
2. ProGuard Rules (if using code obfuscation)
Add the following rules to your android/app/proguard-rules.pro
:
# Keep calendar provider classes
-keep class android.provider.CalendarContract** { *; }
-keep class android.content.ContentValues { *; }
# Keep plugin classes
-keep class com.dovireinfotech.native_calendar.** { *; }
3. Minimum SDK Version
Ensure your android/app/build.gradle
has minimum SDK version 19 or higher:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 19 // Minimum required for calendar features
targetSdkVersion 34
// ... other configurations
}
}
iOS Setup
1. Add Privacy Usage Descriptions
Add the following to your ios/Runner/Info.plist
:
<dict>
<!-- Existing keys... -->
<!-- Calendar access (required) -->
<key>NSCalendarsUsageDescription</key>
<string>Access most functions for calendar viewing and editing.</string>
<!-- iOS 17+: Full Calendar access -->
<key>NSCalendarsFullAccessUsageDescription</key>
<string>Access most functions for calendar viewing and editing.</string>
<!-- Optional: If you want to access reminders as well -->
<key>NSRemindersUsageDescription</key>
<string>This app needs access to reminders to manage calendar events.</string>
</dict>
Note: This plugin uses Swift on iOS. There is a known issue when adding a Swift-based plugin to an Objective‑C project. If you encounter build issues, see Flutter’s guidance on integrating Swift plugins into Objective‑C apps and apply the suggested workarounds.
2. Minimum iOS Version
Ensure your ios/Podfile
targets iOS 12.0 or higher:
platform :ios, '12.0'
3. EventKit Framework
The plugin automatically includes EventKit framework, but you can verify it's included in your ios/Runner.xcodeproj
:
- Open
ios/Runner.xcworkspace
in Xcode - Select your target
- Go to "Build Phases" → "Link Binary With Libraries"
- Ensure
EventKit.framework
andEventKitUI.framework
are listed
Usage
Basic Example
import 'package:flutter_native_calendar/native_calendar.dart';
// Create a simple event
final event = CalendarEvent(
title: 'Team Meeting',
startDate: DateTime.now().add(Duration(hours: 1)),
endDate: DateTime.now().add(Duration(hours: 2)),
description: 'Discuss project progress and next steps',
location: 'Conference Room A', // Simple string location
);
// Option 1: Open calendar app with pre-filled event (recommended)
// Note: On Android, custom reminders may not be respected - calendar app uses its defaults
bool success = await NativeCalendar.openCalendarWithEvent(event);
if (success) {
print('Calendar opened successfully');
} else {
print('Failed to open calendar');
}
// Option 2: Add event directly to calendar (requires permissions)
// Note: This method supports precise reminder control on all platforms
bool hasPermissions = await NativeCalendar.hasCalendarPermissions();
if (!hasPermissions) {
hasPermissions = await NativeCalendar.requestCalendarPermissions();
}
if (hasPermissions) {
bool eventAdded = await NativeCalendar.addEventToCalendar(event);
if (eventAdded) {
print('Event added successfully');
}
}
Structured Location Example
import 'package:flutter_native_calendar/native_calendar.dart';
// Create event with structured location data
final event = CalendarEvent(
title: 'Business Meeting',
startDate: DateTime.now().add(Duration(hours: 2)),
endDate: DateTime.now().add(Duration(hours: 3)),
description: 'Important client meeting',
location: EventLocation.withCoordinates(
title: 'Corporate Headquarters',
address: '123 Business Ave, Suite 500, New York, NY 10001',
latitude: 40.7128,
longitude: -74.0060,
radius: 100.0, // 100 meter radius for geofencing
notes: 'Use the main entrance, ask for visitor pass',
),
);
await NativeCalendar.openCalendarWithEvent(event);
Advanced Example with Platform-Specific Settings
import 'package:flutter_native_calendar/native_calendar.dart';
// Create event with platform-specific settings
final event = CalendarEvent(
title: 'Important Business Meeting',
startDate: DateTime.now().add(Duration(days: 1)),
endDate: DateTime.now().add(Duration(days: 1, hours: 2)),
description: 'Quarterly review meeting',
location: 'Board Room, 15th Floor',
timeZone: 'America/New_York',
url: 'https://zoom.us/j/123456789',
// Android-specific settings
androidSettings: AndroidEventSettings(
reminderMinutes: [15, 60], // 15 minutes and 1 hour before
eventStatus: 1, // confirmed
visibility: 2, // private
hasAlarm: true,
),
// iOS-specific settings
iosSettings: IosEventSettings(
alarmMinutes: [15, 60], // 15 minutes and 1 hour before (max 2 on iOS)
availability: 1, // busy
priority: 1, // high priority
),
);
// Add to calendar - ensures precise reminder control
// Note: Use addEventToCalendar() for exact reminder times,
// or openCalendarWithEvent() for user-controlled creation
bool success = await NativeCalendar.addEventToCalendar(event);
Recurring Event Example
import 'package:flutter_native_calendar/native_calendar.dart';
// Create a recurring event
final recurringEvent = CalendarEvent(
title: 'Weekly Team Meeting',
startDate: DateTime.now().add(Duration(days: 1)),
endDate: DateTime.now().add(Duration(days: 1, hours: 1)),
description: 'Weekly team sync meeting',
location: 'Conference Room A',
iosSettings: IosEventSettings(
alarmMinutes: [15, 5], // Two alarms: 15 min and 5 min before
availability: 1, // busy
hasRecurrenceRules: true,
recurrenceFrequency: RecurrenceFrequency.weekly,
recurrenceInterval: 1, // Every week
recurrenceEndDate: DateTime.now().add(Duration(days: 90)), // End after 3 months
),
);
await NativeCalendar.openCalendarWithEvent(recurringEvent);
Recurrence Frequency
The plugin provides a RecurrenceFrequency
enum for type-safe recurrence patterns:
enum RecurrenceFrequency {
daily, // Every day
weekly, // Every week
monthly, // Every month
yearly, // Every year
}
// Usage
iosSettings: IosEventSettings(
hasRecurrenceRules: true,
recurrenceFrequency: RecurrenceFrequency.weekly,
recurrenceInterval: 2, // Every 2 weeks
)
Android Alarm/Reminder Limitations
Important: There are significant differences between the two calendar integration methods on Android:
openCalendarWithEvent()
- Intent Method (Recommended for User Control)
- ⚠️ Limited Alarm Support: Calendar apps ignore custom reminder times when using Android Intents
- Calendar apps use their own default reminder settings (typically 30 minutes)
- Users can manually adjust reminders in the calendar app interface
- Better for user-controlled event creation with app-specific defaults
addEventToCalendar()
- Direct Addition (Full Alarm Control)
- ✅ Full Alarm Support: Creates exact reminders as specified in
reminderMinutes
- Supports multiple reminders per event
- Requires calendar permissions (
READ_CALENDAR
andWRITE_CALENDAR
) - Events are added directly without user interaction
Recommendation:
- Use
openCalendarWithEvent()
for better user experience and let users set their preferred reminders - Use
addEventToCalendar()
when precise reminder control is required
All-Day Event Example
final allDayEvent = CalendarEvent(
title: 'Company Holiday',
startDate: DateTime(2024, 12, 25),
isAllDay: true,
description: 'Christmas Day - Office Closed',
);
await NativeCalendar.openCalendarWithEvent(allDayEvent);
Permission Handling
// Check if permissions are granted
bool hasPermissions = await NativeCalendar.hasCalendarPermissions();
if (!hasPermissions) {
// Request permissions
bool granted = await NativeCalendar.requestCalendarPermissions();
if (granted) {
// Proceed with calendar operations
print('Calendar permissions granted');
} else {
// Handle permission denial
print('Calendar permissions denied');
// Show explanation dialog or redirect to settings
}
}
API Reference
CalendarEvent
Property | Type | Description | Required |
---|---|---|---|
title |
String |
Event title | ✅ |
startDate |
DateTime |
Event start date and time | ✅ |
endDate |
DateTime? |
Event end date and time | ❌ |
description |
String? |
Event description/notes | ❌ |
location |
String? or EventLocation? |
Event location (simple string or structured data) | ❌ |
isAllDay |
bool |
Whether event is all-day (default: false) | ❌ |
timeZone |
String? |
Timezone identifier | ❌ |
url |
String? |
Associated URL | ❌ |
androidSettings |
AndroidEventSettings? |
Android-specific settings | ❌ |
iosSettings |
IosEventSettings? |
iOS-specific settings | ❌ |
EventLocation
Property | Type | Description | Required |
---|---|---|---|
title |
String |
Display name of the location | ✅ |
address |
String? |
Full address | ❌ |
latitude |
double? |
Latitude coordinate (-90 to 90) | ❌ |
longitude |
double? |
Longitude coordinate (-180 to 180) | ❌ |
radius |
double? |
Radius in meters (for geofencing) | ❌ |
notes |
String? |
Additional location notes | ❌ |
EventLocation Constructors:
EventLocation.simple(String title)
- Creates a basic location with just a titleEventLocation.withCoordinates(...)
- Creates a location with GPS coordinates and full details
AndroidEventSettings
Property | Type | Description | Default |
---|---|---|---|
calendarId |
int? |
Target calendar ID | null (default) |
eventStatus |
int |
Event status (0=tentative, 1=confirmed, 2=canceled) | 1 |
visibility |
int |
Visibility (0=default, 1=confidential, 2=private, 3=public) | 0 |
hasAlarm |
bool |
Whether to set reminders | true |
reminderMinutes |
List<int>? |
Reminder times in minutes before event | [15] |
eventColor |
int? |
Event color as integer | null |
IosEventSettings
Property | Type | Description | Default |
---|---|---|---|
calendarIdentifier |
String? |
Target calendar identifier | null (default) |
availability |
int |
Availability (1=busy, 2=free, 3=tentative, 4=unavailable) | 1 |
alarmMinutes |
List<int>? |
Alarm times in minutes before event (max 2 alarms) | [15] |
priority |
int |
Priority (1-4=High, 5=Normal, 6-9=Low) - added to notes | 5 |
hasRecurrenceRules |
bool |
Whether event has recurrence | false |
recurrenceFrequency |
RecurrenceFrequency? |
Recurrence frequency enum (daily, weekly, monthly, yearly) | null |
recurrenceInterval |
int? |
Recurrence interval (e.g., every 2 weeks) | null |
recurrenceEndDate |
DateTime? |
Recurrence end date | null |
Methods
NativeCalendar.openCalendarWithEvent(CalendarEvent event)
Opens the native calendar app with pre-filled event details. User can review and save manually.
- Returns:
Future<bool>
- true if calendar opened successfully - Permissions: Not required (but recommended for better UX)
NativeCalendar.addEventToCalendar(CalendarEvent event)
Adds event directly to calendar without user interaction.
- Returns:
Future<bool>
- true if event was added successfully - Permissions: Required (WRITE_CALENDAR, READ_CALENDAR)
NativeCalendar.hasCalendarPermissions()
Checks if calendar permissions are granted.
- Returns:
Future<bool>
- true if permissions granted
NativeCalendar.requestCalendarPermissions()
Requests calendar permissions from user.
- Returns:
Future<bool>
- true if permissions granted
Troubleshooting
Android Issues
- Permission Denied: Ensure you've added calendar permissions to AndroidManifest.xml
- Calendar Not Opening: Check if device has a calendar app installed
- Events Not Saving: Verify WRITE_CALENDAR permission is granted
iOS Issues
- Permission Denied: Ensure NSCalendarsUsageDescription is added to Info.plist
- Calendar Not Opening: Ensure EventKit/EventKitUI frameworks are linked
- Priority Support: iOS EventKit doesn't support direct event priority. Priority is added as text to event notes (e.g., "
High Priority
") - Alarm Limitations: iOS allows maximum 2 alarms per event. Additional alarms in the list will be ignored
- Recurrence Support: iOS supports recurring events through EKRecurrenceRule
- iOS Simulator: Calendar permissions might behave differently on simulator vs device
General Issues
- Plugin Not Found: Run
flutter clean
andflutter pub get
- Build Errors: Ensure minimum SDK/iOS versions are met
- Date Issues: Always use UTC or properly handle timezones
Example App
See the example directory for a complete working app demonstrating all features.
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.
License
This project is licensed under the MIT License - see the LICENSE file for details.