Skip to content

A comprehensive Flutter plugin for accessing and managing device calendars on Android, iOS, and macOS with clean architecture principles.

License

Notifications You must be signed in to change notification settings

JenteJan/calendar_bridge

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Calendar Bridge

pub package Platform

A comprehensive Flutter plugin for accessing and managing device calendars on Android, iOS, and macOS with clean architecture principles.

Table of Contents

Features

  • Calendar Management: Create, retrieve, and delete calendars
  • Event CRUD Operations: Full create, read, update, delete support for events
  • Recurring Events: Support for recurring events using RRULE format
  • Timezone Support: Full timezone handling with TZDateTime
  • Attendee Management: Add and manage event attendees
  • Reminders: Set and manage event reminders
  • Permission Handling: Proper permission management across platforms
  • Calendar Colors: Support for calendar and event colors
  • Clean Architecture: Built with domain-driven design principles
  • Well Tested: Comprehensive test coverage

Platform Support

Platform Supported Notes
Android Yes API 21+
iOS Yes iOS 13+
macOS Yes macOS 11+

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  calendar_bridge: ^1.0.0

Then run:

flutter pub get

Platform Setup

iOS & macOS

Add the following keys to your Info.plist file:

<key>NSCalendarsUsageDescription</key>
<string>This app requires access to your calendar to manage your events.</string>
<!-- Full access for macOS 14+ -->
<key>NSCalendarsFullAccessUsageDescription</key>
<string>This app requires full calendar access to create, edit, and delete your events.</string>
<!-- Additional write access -->
<key>NSCalendarsWriteOnlyAccessUsageDescription</key>
<string>This app requires access to create calendar events.</string>
<key>NSRemindersUsageDescription</key>
<string>This app needs access to reminders to set event notifications</string>

Android

Add the following permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />

Usage

Basic Setup

import 'package:calendar_bridge/calendar_bridge.dart';

final calendarApi = CalendarBridge();

Permission Handling

Always request permissions before accessing calendar data:

// Check if permissions are granted
final hasPermissions = await calendarApi.hasPermissions();
if (hasPermissions != PermissionStatus.granted) {
  // Request permissions
  final granted = await calendarApi.requestPermissions();
  if (!granted) {
    // Handle permission denied
    return;
  }
}

Working with Calendars

Get All Calendars

try {
  final calendars = await calendarApi.getCalendars();
  for (final calendar in calendars) {
    print('Calendar: ${calendar.name} (${calendar.id})');
  }
} catch (e) {
  print('Error getting calendars: $e');
}

Create a New Calendar

try {
  final newCalendar = await calendarApi.createCalendar(
    name: 'My Custom Calendar',
    color: 0xFF2196F3, // Blue color
    localAccountName: 'Local',
  );
  print('Created calendar: ${newCalendar.name}');
} catch (e) {
  print('Error creating calendar: $e');
}

Get Default Calendar

try {
  final defaultCalendar = await calendarApi.getDefaultCalendar();
  print('Default calendar: ${defaultCalendar.name}');
} catch (e) {
  print('Error getting default calendar: $e');
}

Working with Events

Create a Simple Event

try {
  final eventId = await calendarApi.createSimpleEvent(
    calendarId: calendar.id,
    title: 'Team Meeting',
    start: DateTime.now().add(Duration(hours: 1)),
    end: DateTime.now().add(Duration(hours: 2)),
    description: 'Weekly team sync',
    location: 'Conference Room A',
  );
  print('Created event with ID: $eventId');
} catch (e) {
  print('Error creating event: $e');
}

Create a Complex Event with Attendees and Reminders

final event = CalendarEvent(
  calendarId: calendar.id,
  title: 'Project Review',
  description: 'Quarterly project review meeting',
  start: TZDateTime.from(DateTime.now().add(Duration(days: 1)), UTC),
  end: TZDateTime.from(DateTime.now().add(Duration(days: 1, hours: 2)), UTC),
  location: 'Meeting Room B',
  attendees: [
    Attendee(
      name: 'John Doe',
      email: 'john@example.com',
      role: AttendeeRole.required,
    ),
    Attendee(
      name: 'Jane Smith',
      email: 'jane@example.com',
      role: AttendeeRole.optional,
    ),
  ],
  reminders: [
    Reminder(minutes: 15), // 15 minutes before
    Reminder(minutes: 60), // 1 hour before
  ],
);

try {
  final eventId = await calendarApi.createEvent(event);
  print('Created complex event with ID: $eventId');
} catch (e) {
  print('Error creating event: $e');
}

Get Events from a Calendar

try {
  // Get all events
  final allEvents = await calendarApi.getEvents(calendar.id);
  
  // Get events in a date range
  final eventsInRange = await calendarApi.getEvents(
    calendar.id,
    startDate: DateTime.now(),
    endDate: DateTime.now().add(Duration(days: 30)),
  );
  
  // Get today's events
  final todaysEvents = await calendarApi.getTodaysEvents(calendar.id);
  
  // Get upcoming events (next 7 days by default)
  final upcomingEvents = await calendarApi.getUpcomingEvents(calendar.id);
  
  print('Found ${allEvents.length} total events');
} catch (e) {
  print('Error getting events: $e');
}

Update an Event

// First, get the event
final events = await calendarApi.getEvents(calendar.id);
if (events.isNotEmpty) {
  final eventToUpdate = events.first.copyWith(
    title: 'Updated Event Title',
    description: 'Updated description',
  );
  
  try {
    await calendarApi.updateEvent(eventToUpdate);
    print('Event updated successfully');
  } catch (e) {
    print('Error updating event: $e');
  }
}

Delete an Event

try {
  final success = await calendarApi.deleteEvent(calendar.id, eventId);
  if (success) {
    print('Event deleted successfully');
  }
} catch (e) {
  print('Error deleting event: $e');
}

Recurring Events

Calendar Bridge supports recurring events using the RRULE standard:

import 'package:rrule/rrule.dart';

final recurringEvent = CalendarEvent(
  calendarId: calendar.id,
  title: 'Daily Standup',
  start: TZDateTime.from(DateTime.now(), UTC),
  end: TZDateTime.from(DateTime.now().add(Duration(minutes: 30)), UTC),
  recurrenceRule: RecurrenceRule(
    frequency: Frequency.daily,
    count: 30, // Repeat 30 times
  ),
);

await calendarApi.createEvent(recurringEvent);

Calendar Colors

Get and set calendar colors:

// Get available calendar colors
final calendarColors = await calendarApi.getCalendarColors();
print('Available calendar colors: $calendarColors');

// Get available event colors for a calendar
final eventColors = await calendarApi.getEventColors(calendar.id);
print('Available event colors: $eventColors');

// Update calendar color
if (calendarColors != null && calendarColors.isNotEmpty) {
  final colorKey = calendarColors.keys.first;
  await calendarApi.updateCalendarColor(calendar.id, colorKey);
}

Error Handling

Calendar Bridge provides specific exception types for better error handling:

try {
  final calendars = await calendarApi.getCalendars();
} on PermissionDeniedException {
  print('Calendar permissions are required');
} on CalendarNotFoundException {
  print('Calendar not found');
} on EventNotFoundException {
  print('Event not found');
} on InvalidArgumentException {
  print('Invalid arguments provided');
} catch (e) {
  print('Unexpected error: $e');
}

Models

Calendar

class Calendar {
  final String id;
  final String name;
  final int? color;
  final String? accountName;
  final String? accountType;
  final bool isReadOnly;
  final bool isDefault;
}

CalendarEvent

class CalendarEvent {
  final String calendarId;
  final String? eventId;
  final String? title;
  final String? description;
  final TZDateTime? start;
  final TZDateTime? end;
  final bool allDay;
  final String? location;
  final String? url;
  final RecurrenceRule? recurrenceRule;
  final List<Attendee> attendees;
  final List<Reminder> reminders;
  final EventStatus? eventStatus;
  final Availability? availability;
  final String? organizer;
  final String? eventColor;
}

Attendee

class Attendee {
  final String? name;
  final String? email;
  final AttendeeRole? role;
  final AttendeeStatus? status;
}

Reminder

class Reminder {
  final int minutes; // Minutes before event start
}

Example App

The plugin comes with a comprehensive example app that demonstrates all features. To run the example:

cd example
flutter run

The example app includes:

  • Calendar list view
  • Event management (create, edit, delete)
  • Calendar view with monthly grid
  • Settings and permissions handling

Testing

Calendar Bridge includes comprehensive test coverage. Run tests with:

flutter test

For integration tests:

cd example
flutter test integration_test/

Architecture

This plugin follows clean architecture principles:

  • Domain Layer: Contains business logic, entities, and use cases
  • Infrastructure Layer: Platform-specific implementations
  • API Layer: Simple, clean interface for consumers

Contributing

Contributions are welcome! Please read the contributing guidelines and submit pull requests to the GitHub repository.

Changelog

See CHANGELOG.md for a detailed list of changes.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

If you encounter any issues or have questions, please file an issue on GitHub.

About

A comprehensive Flutter plugin for accessing and managing device calendars on Android, iOS, and macOS with clean architecture principles.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 78.4%
  • Swift 9.5%
  • Kotlin 9.5%
  • Shell 1.8%
  • Other 0.8%