Kalender Flutter Calendar Widget

repository·main·Indexed 41 days ago

https://github.com/werner-scholtz/kalender

A highly customizable Flutter calendar widget featuring multiple views (Day, Month, Schedule, etc.), drag-and-drop rescheduling, and timezone awareness. It includes a flexible event model via KalenderEvent, support for iCalendar (.ics) files, and integration options for intl4x localization and the material_ui package.

Tokens
36.2K
Snippets
97
Records
123
Agent score
51%

What's inside Kalender

  1. Overview of the Kalender web demo capabilities

    main

    The web demo serves as a showcase for the kalender package, demonstrating its ability to adapt to touch devices and various view types. Key features demonstrated include:

    • Runtime Configuration: Using DemoConfiguration to toggle interaction modes, snapping behavior, and visibility.
    • Localization: Switching between themes, locales, and text directions (LTR/RTL).
    • Responsive Layouts: A desktop split view featuring two independent calendars sharing a single event store, and a single-calendar layout optimized for mobile.
    • Timezone Support: Timezone-aware events using a timezone package that is bootstrapped via conditional imports for browser, IO, and stub targets.
  2. Overview of Kalender example projects

    main

    The following table summarizes the different example projects available in the repository and the specific features or patterns they demonstrate:

    ExampleDemonstrates
    exampleRecommended starting point. Shows all view types with a toolbar, sample events, custom tiles, and create, drag, and resize functionality.
    advanced_exampleCustom event layouts (e.g., one lane per person), zoom behavior for desktop and mobile, and context-aware event creation (e.g., pre-assigning an event to a tapped lane).
    recurrenceImplementing recurring events. Since the package does not handle recurrence internally, this shows how to generate occurrences up front.
    icsiCalendar (iCal) integration. Uses enough_icalendar for parsing/writing and rrule for expansion, generating occurrences only for the visible window to maintain performance.
    riverpodState management using Riverpod to share the events controller, calendar controller, and selected view through providers.
    intl4xLocalization using intl4x instead of the standard intl package.
    web_demoThe source for the live demo. Demonstrates runtime configuration, theming, locales, text direction, timezones, and desktop split views.
    material_uiIntegration within an app using the standalone material_ui package, including workarounds for compatibility issues with package:flutter/material.dart.
    testingA performance harness that drives the calendar through navigation, scrolling, rescheduling, and resizing to record frame build times.
    doc_snippetsTechnical utility containing placeholder identifiers used by tool/analyze_doc_snippets.dart to compile documentation snippets.
  3. Core Features of Kalender

    main

    Kalender provides a highly customizable calendar experience with the following capabilities:

    • Multiple Views: Supports Day, Multi-day (week, work week, etc.), Month, and Schedule views within a single widget.
    • Flexible Event Model: Uses a non-fixed model where you subclass KalenderEvent to attach custom data fields.
    • Interactive Rescheduling: Supports drag-and-drop rescheduling, event resizing, and zooming via mouse, stylus, trackpad, or touch.
    • Customizable Snapping: Control how events snap to intervals, the time indicator, other events, or custom rules.
    • Timezone & Locale Awareness: Events are stored in UTC and can be displayed in any IANA location. Supports intl for localization and right-to-left layouts.
    • Theming: Uses Material 3 by default and follows your app's ThemeData. Most components (event tiles, headers, gutters) are replaceable.
  4. How to handle time zones in iCalendar files

    main

    When working with .ics files, be aware of how time zones are handled:

    • Floating Times: If the file contains no time zone information, times are treated as local wall-clock time.
    • Time Zone IDs (TZID): Real .ics files often carry a TZID. To map these correctly, you should build TZDateTime objects using the timezone package before passing the data to kalender.
  5. Implement a custom event layout strategy

    main

    You can customize how events are displayed within a day column by providing a custom layout strategy to MultiDayBodyConfiguration.eventLayoutStrategy.

    In the provided advanced example, CustomSideBySideLayoutDelegate (found in lib/layout_strategy.dart) is used to split each day column into individual lanes, one per person, allowing for a side-by-side view of schedules.

    // Example concept: using a custom layout strategy
    MultiDayBodyConfiguration(
      eventLayoutStrategy: CustomSideBySideLayoutDelegate(),
    )
  6. Configure shared options for all views

    main

    All view configurations (MultiDay, Month, and Schedule) accept the following shared options:

    • displayRange: The total date range the calendar can navigate (e.g., Jan 2024 to Dec 2025). Defaults to 2 years before and 2 years after the current date.
    • initialDateTime: The date shown on the first render. Defaults to DateTime.now().
    • multiDayRule: Determines what counts as a multi-day event for the header.
      • MultiDayRule.minimumDuration (default): Events lasting 24+ hours.
      • MultiDayRule.calendarDays(): Anything crossing midnight.
    • name: A unique identifier for the view (e.g., 'Day', 'Week', 'Month'). This is used by DateTransition.restorePerView to match saved dates to specific views.
    • nowCallback: Overrides how the calendar resolves the current time.
  7. Limitations of intl4x localization

    main

    When using intl4x for localization, be aware of the following limitations:

    • Weekday names: intl4x (via DisplayNames) does not support weekday-only formatting. Kalender handles day headers using MaterialLocalizations.narrowWeekdays instead.
    • Month names: DateTimeFormat.month defaults to DateTimeLength.short, which may result in numeric outputs (e.g., 1 for January). To get full names (e.g., January), use DateTimeLength.long.
  8. How recurrence is implemented in Kalender

    main

    Since kalender does not have built-in recurrence support, recurrence is implemented as a layer on top of the core library using three main components:

    1. Recurrence: A model describing the rule, including the type (e.g., none, daily, weekly), the first occurrence, and the total count.
    2. RecurringCalendarEvent: Concrete events generated from a Recurrence rule. All events generated from the same rule share a groupId, allowing for 'fan-out' operations where editing or deleting one event affects the entire group.
    3. RecurrenceController: A controller (found in lib/recurrence.dart) that manages the groups and synchronizes the main events controller when recurrences are created, edited, or removed.

    Note on Scaling: This implementation generates occurrences up front, making it suitable for a small, bounded number of repeats. For open-ended rules (like iCalendar RRULE patterns), use the ics example approach which expands rules lazily over the visible range instead.

  9. Handle multi-day and all-day events

    main

    Events can be categorized as multi-day or all-day using the following logic:

    • All-day events: Set isAllDay: true in the constructor. This forces the event into the header lane regardless of its duration. For midnight-to-midnight accuracy, provide the appropriate date range.
    • Multi-day rules: A MultiDayRule determines if an event renders in the multi-day header lane or the day timeline. By default, events 24 hours or longer are treated as multi-day.
    • Overriding rules: An individual event can override the global view configuration by setting its own multiDayRule. If multiDayRule is null, the calendar's default rule applies.

    To check if an event is currently considered multi-day, use spansMultipleDays(). You should pass the calendar's location and the viewConfiguration.multiDayRule to ensure timezone-aware calculations (especially for MultiDayRule.calendarDays).

    // All-day event
    KalenderEvent(start: range.start, end: range.end, isAllDay: true)
    
    // Overriding the rule for a specific event
    KalenderEvent(
      start: range.start, 
      end: range.end, 
      multiDayRule: const MultiDayRule.calendarDays(),
    )
    
    // Checking if an event spans multiple days
    event.spansMultipleDays(location: location, defaultRule: viewConfiguration.multiDayRule)
  10. Use custom fields with KalenderEvent

    main
    While the base KalenderEvent class can be used for simple implementations, most real-world applications require custom data. To add custom fields to your events, you must subclass KalenderEvent. Detailed instructions for subclassing can be found in the project's Events guide.
  11. Implement zoom functionality with ZoomDetector

    main

    To add zooming capabilities to the calendar, use a ZoomDetector (found in lib/zoom.dart). This implementation supports:

    • Desktop: ctrl + scroll or trackpad pinch.
    • Mobile: Two-finger pinch.

    The detector adjusts the height-per-minute and ensures the scroll position remains anchored under the user's pointer during the zoom operation.

  12. Migrate from v0.27.x to v0.28.0: Update EventInteractionGesture type

    main

    The enum CreateEventGesture has been renamed to EventInteractionGesture. This change reflects that the enum handles both event creation and modification. The fields createEventGesture and modifyEventGesture on CalendarInteraction retain their original names, but their types have changed.

    // Before
    CalendarInteraction(
      createEventGesture: CreateEventGesture.tap,
      modifyEventGesture: CreateEventGesture.longPress,
    );
    
    // After
    CalendarInteraction(
      createEventGesture: EventInteractionGesture.tap,
      modifyEventGesture: EventInteractionGesture.longPress,
    );