infinite_calendar_view 2.5.4 copy "infinite_calendar_view: ^2.5.4" to clipboard
infinite_calendar_view: ^2.5.4 copied to clipboard

Easily build your different views of your calendar with this package. Month, week, three days, one day, list ... and many features.

Plugin Banner

infinite_calendar_view #

Build infinite_calendar_view

A Flutter package allows you to easily implement all calendar UI.

Web Demo #

Features #

  • πŸ’™ Inspired by Outlook and Teams mobile. Easy to use
  • 🍻 Three views. Planner, Month and List views
  • ♾️ Infinite scroll. Lazy build and best user experience
  • πŸš€ Good performance. With several dozen appointments per day
  • ✏️ All configurable. Everything is configurable!
  • πŸ—‚οΈ Events filter. Easy filter day events
  • πŸ—“οΈ Events types. Managing all-day events and multi-day events
  • 🎲 Customizable number of days. Depending on the screen size
  • 🀏 Pinch to zoom. Change the time scale with two fingers
  • πŸ‘†πŸΌ Drag and drop. Move appointments easily
  • πŸ‘©β€πŸ‘§β€πŸ‘¦ Multi columns. Manage multiple calendars in the same view !
  • πŸ—“οΈ Events arranger. Customize the placement of appointments in the schedule
  • ♾️ ...

Preview #

Calendar views
1 day
drawing
3 days
drawing
Month
drawing
List
drawing
7 day (web or tablet)
drawing

Installing #

  1. Add dependencies to pubspec.yaml

    Get the latest version in the 'Installing' tab on pub.dev

    dependencies:
        infinite_calendar_view: <latest-version>
    
    copied to clipboard
  2. Run pub get.

    flutter pub get
    
    copied to clipboard
  3. Import package.

    import 'package:infinite_calendar_view/infinite_calendar_view.dart';
    
    copied to clipboard

Implementation #

Make sure to check out examples and API docs for more details.

  1. Init controller.

     EventsController controller = EventsController();
    
    copied to clipboard
  2. Add calendar views.

    For 1 Day Planner View

    Scaffold(
        body: EventsPlanner(
          controller: controller,
          daysShowed : 1
        ),
    );
    
    copied to clipboard

    For 3 Days Planner View

    Scaffold(
        body: EventsPlanner(
          controller: controller,
          daysShowed : 3
        ),
    );
    
    copied to clipboard

    For List View

    Scaffold(
        body: EventsList(
          controller: controller,
        ),
    );
    
    copied to clipboard
  3. Use controller to add or remove events when you want.

    To Add event:

    final event = Event(
       startTime: DateTime(2024, 8, 10, 8, 0),
       endTime: DateTime(2024, 8, 10, 9, 0),
       title: "Event1"
    );
    
    controller.updateCalendarData((calendarData) {
      calendarData.addEvents([event]);
    });
    
    copied to clipboard

    To Add full day event:

    final fullDayEvent = FullDayEvent(
       title: "Full Day Event1"
    );
    
    controller.updateCalendarData((calendarData) {
      calendarData.addFullDayEvents(DateTime(2024, 8, 10), [event]);
    });
    
    copied to clipboard

    As soon as you add or remove events from the controller, it will automatically update the calendar view assigned to that controller.

  4. Use GlobalKey to jump to a specific date.

     GlobalKey<EventsPlannerState> key = GlobalKey<EventsPlannerState>();
    
    copied to clipboard

    Add key in EventsPlanner or EventsList

     EventsPlanner(
       key : key
       ...
     )
    
    copied to clipboard

    Jump to date

     key.currentState?.jumpToDate(DateTime(2024, 8, 10))
    
    copied to clipboard

More on the infinite calendar view #

  1. For Pinch To Zoom

    EventsPlanner(
       controller: controller,
       pinchToZoomParam: PinchToZoomParameters(
          pinchToZoom: true,
          onZoomChange: (heightPerMinute) {},
          pinchToZoomMinHeightPerMinute: 0.5,
          pinchToZoomMaxHeightPerMinute: 2.5,
          pinchToZoomSpeed: 1,
       ),
    );
    
    copied to clipboard
  2. For Draggable Event

    EventsPlanner(
       controller: controller,
       dayParam: DayParam(
         dayEventBuilder: (event, height, width, heightPerMinute) {
           return DraggableEventWidget(
             event: event,
             height: height,
             width: width,
             heightPerMinute: heightPerMinute,
             onDragEnd: (exactStartDateTime, exactEndDateTime, roundStartDateTime, roundEndDateTime) {
               moveEvent(event, roundStartDateTime, roundEndDateTime);
             },
             child: DefaultDayEvent(
               height: height,
               width: width,
               title: event.title,
               description: event.description,
             ),
           );
         },
       ),
    );
    
    copied to clipboard
    void moveEvent(Event oldEvent, DateTime roundStartDateTime, DateTime roundEndDateTime) {
      controller.updateCalendarData((calendarData) {
        calendarData.updateEvent(
          oldEvent: oldEvent,
          newEvent: oldEvent.copyWith(
            startTime: roundStartDateTime,
            endTime: roundEndDateTime,
          ),
        );
      });
    }
    
    copied to clipboard
  3. Manage multiple calendars in the same view with multiple columns

    Set columnIndex in Event

    Event(
       columnIndex: 2,
       ...
     )
    
    copied to clipboard

    Set columns number, labels, colors and ratio (optional)

    EventsPlanner(
       controller: controller,
       columnsParam: ColumnsParam(
         columns: 4,
         columnsLabels: ["Tennis", "Foot", "Bad"],
         columnsWidthRatio: [1 / 3, 1 / 3, 1 / 3],
         columnsColors: [
           Colors.yellow.pastel,
           Colors.green.pastel,
           Colors.blueAccent.pastel,
         ],
       ),
    );
    
    copied to clipboard
    drawing drawing drawing

All parameters #

  1. Events planner all parameters

    EventsPlanner(
       key: GlobalKey<EventsPlannerState>(),
       controller: controller,
       daysShowed: 3,
       initialDate: DateTime.now(),
       maxNextDays: 365,
       maxPreviousDays: 365,
       heightPerMinute: 1.0,
       initialVerticalScrollOffset: 1.0 * 7 * 60,
       daySeparationWidth: 3,
       onDayChange: (firstDay) {},
       onVerticalScrollChange: (offset) {},
       automaticAdjustHorizontalScrollToDay: true,
       onAutomaticAdjustHorizontalScroll: (day) {},
       horizontalScrollPhysics: const BouncingScrollPhysics(
         decelerationRate: ScrollDecelerationRate.fast,
       ),
       dayEventsArranger: SideEventArranger(paddingLeft: 0, paddingRight: 0),
       daysHeaderParam: DaysHeaderParam(
         daysHeaderVisibility: true,
         daysHeaderHeight: 40.0,
         daysHeaderColor: Theme.of(context).appBarTheme.backgroundColor,
         dayHeaderBuilder: (day, isToday) {
           return DefaultDayHeader(
             dayText: DateFormat("E d").format(day),
             isToday: isToday,
             foregroundColor: Colors.white,
           );
         },
       ),
       offTimesParam: OffTimesParam(
         offTimesAllDaysRanges: [
           OffTimeRange(
             TimeOfDay(hour: 0, minute: 0),
             TimeOfDay(hour: 7, minute: 0),
           ),
           OffTimeRange(
             TimeOfDay(hour: 18, minute: 0),
             TimeOfDay(hour: 24, minute: 0),
           )
         ],
         offTimesDayRanges: {
           DateTime(2024, 10, 8): [
             OffTimeRange(
               TimeOfDay(hour: 8, minute: 0),
               TimeOfDay(hour: 18, minute: 0),
             ),
           ],
         },
         offTimesColor: Color(0xFFF4F4F4),
         offTimesAllDaysPainter: (isToday, heightPerMinute, ranges, color) =>
             OffSetAllDaysPainter(
                 isToday, heightPerMinute, [], Color(0xFFF4F4F4)),
         offTimesDayPainter: (isToday, heightPerMinute, ranges, color) =>
             OffSetAllDaysPainter(
                 isToday, heightPerMinute, [], Color(0xFFF4F4F4)),
       ),
       dayParam: DayParam(
         todayColor: Colors.black12,
         dayTopPadding: 10,
         dayBottomPadding: 15,
         onSlotMinutesRound: 15,
         onSlotTap: (columnIndex, exactDateTime, roundDateTime) {},
         onSlotLongTap: (columnIndex, exactDateTime, roundDateTime) {},
         onSlotDoubleTap: (columnIndex, exactDateTime, roundDateTime) {},
         onDayBuild: (day) {},
         dayEventBuilder: (event, height, width, heightPerMinute) {
           return DefaultDayEvent(
             height: height,
             width: width,
             title: event.title,
             description: event.description,
           );
         },
         dayCustomPainter: (heightPerMinute, isToday) => LinesPainter(
           heightPerMinute: heightPerMinute,
           isToday: isToday,
           lineColor: Colors.black12,
         ),
       ),
       fullDayParam: FullDayParam(
         fullDayEventsBarVisibility: true,
         fullDayEventsBarLeftText: 'All day',
         fullDayEventsBarLeftWidget: Text('All day'),
         fullDayEventsBarHeight: 40,
         fullDayEventsBarDecoration: const BoxDecoration(
             border: Border(bottom: BorderSide(color: Colors.black12))),
       ),
       timesIndicatorsParam: TimesIndicatorsParam(
         timesIndicatorsWidth: 60.0,
         timesIndicatorsHorizontalPadding: 4.0,
         timesIndicatorsCustomPainter: (heightPerMinute) => HoursPainter(
           heightPerMinute: 1.0,
           showCurrentHour: true,
           hourColor: Colors.black12,
           halfHourColor: Colors.black12,
           quarterHourColor: Colors.black12,
           currentHourIndicatorColor: Colors.black12,
           halfHourMinHeightPerMinute: 1.3,
           quarterHourMinHeightPerMinute: 2,
         ),
       ),
       currentHourIndicatorParam: CurrentHourIndicatorParam(
         currentHourIndicatorHourVisibility: true,
         currentHourIndicatorLineVisibility: true,
         currentHourIndicatorColor: Colors.blue,
         currentHourIndicatorCustomPainter: (heightPerMinute, isToday) =>
             TimeIndicatorPainter(heightPerMinute, isToday, Colors.blue),
       ),
       pinchToZoomParam: PinchToZoomParameters(
         pinchToZoom: true,
         onZoomChange: (heightPerMinute) {},
         pinchToZoomMinHeightPerMinute: 0.5,
         pinchToZoomMaxHeightPerMinute: 2.5,
         pinchToZoomSpeed: 1,
       ),
       columnsParam: ColumnsParam(
         columns: 2,
         columnsLabels: ["Tennis", "Foot"],
         columnsColors: [Colors.blue, Colors.red],
         columnsForegroundColors: [Colors.white, Colors.white],
         columnsWidthRatio: [1 / 2, 1 / 2],
         columnHeaderBuilder: (day, isToday, columIndex, columnWidth) {
           return DefaultColumnHeader(
             backgroundColor: Colors.transparent,
             foregroundColor: Colors.white,
             columnText: ["Tennis", "Foot"].elementAt(columIndex),
             columnWidth: columnWidth,
           );
         },
         columnCustomPainter : (double width, int colum) => ColumnPainter(
            width: width,
            columnsParam: columnsParam,
            lineColor: Colors.gray,
         ),
       ),
    );
    
    copied to clipboard
  2. Events List all parameters

    EventsList(
       controller: controller,
       initialDate: DateTime.now(),
       maxPreviousDays: 365,
       maxNextDays: 365,
       onDayChange: (day) {},
       todayHeaderColor: const Color(0xFFf4f9fd),
       verticalScrollPhysics: const BouncingScrollPhysics(
         decelerationRate: ScrollDecelerationRate.fast,
       ),
       dayEventsBuilder: (day, events) {
         return DefaultDayEvents(
           events: events,
           eventBuilder: (event) => DefaultDetailEvent(event: event),
           nullEventsWidget: DefaultDayEvents.defaultEmptyEventsWidget,
           eventSeparator: DefaultDayEvents.defaultEventSeparator,
           emptyEventsWidget: DefaultDayEvents.defaultEmptyEventsWidget,
         );
       },
       dayHeaderBuilder: (day, isToday) => DefaultHeader(
         dayText: DateFormat.MMMMEEEEd().format(day).toUpperCase(),
       ),
    );
    
    copied to clipboard

Local #

The package does not support locales itself, so as not to import additional dependencies like intl which may cause dependency conflicts.

However, all texts are easily customizable. You can use intl to translate the texts into the desired language. Local example

Showcase #

drawing drawing

License #

MIT License

Copyright (c) 2025 Pickywawa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

copied to clipboard
65
likes
160
points
521
downloads

Publisher

unverified uploader

Weekly Downloads

2024.10.05 - 2025.04.19

Easily build your different views of your calendar with this package. Month, week, three days, one day, list ... and many features.

Repository (GitHub)
View/report issues
Contributing

Documentation

API reference

License

MIT (license)

Dependencies

flutter, sticky_infinite_list

More

Packages that depend on infinite_calendar_view