πŸ“… Customizable flutter calendar widget including day and week views

Last update: Jul 11, 2022

πŸ“… Customizable, animated calendar widget including day, week, and month views.

Navigation Animation
Callbacks Changing the VisibleDateRange

Available Layouts

MultiDateTimetable

A Timetable widget that displays multiple consecutive days.

Light Mode Dark Mode

RecurringMultiDateTimetable

A Timetable widget that displays multiple consecutive days without their dates and without a week indicator.

Light Mode Dark Mode

CompactMonthTimetable

A Timetable widget that displays MonthWidgets in a page view.

Light Mode Dark Mode

Getting started

0. General Information

Timetable doesn't care about any time-zone related stuff. All supplied DateTimes must have isUtc set to true, but the actual time zone is then ignored when displaying events.

Some date/time-related parameters also have special suffixes:

  • date: A DateTime with a time of zero.
  • month: A DateTime with a time of zero and a day of one.
  • timeOfDay: A Duration between zero and 24 hours.
  • dayOfWeek: An int between one and seven (DateTime.monday through DateTime.sunday).

Timetable currently offers localizations for Chinese, English, French, German, Hungarian, Italian, Japanese, Portuguese, and Spanish. Even if you're just supporting English in your app, you have to add Timetable's localization delegate to your MaterialApp/CupertinoApp/WidgetsApp:

MaterialApp(
  localizationsDelegates: [
    TimetableLocalizationsDelegate(),
    // Other delegates, e.g., `GlobalMaterialLocalizations.delegate`
  ],
  // ...
);

You want to contribute a new localization? Awesome! Please follow the steps listed in the doc comment of TimetableLocalizationsDelegate.

1. Define your Events

Events are provided as instances of Event. To get you started, there's the subclass BasicEvent, which you can instantiate directly. If you want to be more specific, you can also implement your own class extending Event.

⚠️ Most of Timetable's classes accept a type-parameter E extends Event. Please set it to your chosen Event-subclass (e.g., BasicEvent) to avoid runtime exceptions.

In addition, you also need a Widget to display your events. When using BasicEvent, this can simply be BasicEventWidget.

2. Create a DateController (optional)

Similar to a ScrollController or a TabController, a DateController is responsible for interacting with Timetable's widgets and managing their state. As the name suggests, you can use a DateController to access the currently visible dates, and also animate or jump to different days. And by supplying a VisibleDateRange, you can also customize how many days are visible at once and whether they, e.g., snap to weeks.

final myDateController = DateController(
  // All parameters are optional and displayed with their default value.
  initialDate: DateTimeTimetable.today(),
  visibleRange: VisibleDateRange.week(startOfWeek: DateTime.monday),
);

Don't forget to dispose your controller, e.g., in State.dispose!

Here are some of the available VisibleDateRanges:

  • VisibleDateRange.days: displays visibleDayCount consecutive days, snapping to every swipeRange days (aligned to alignmentDate) in the range from minDate to maxDate
  • VisibleDateRange.week: displays and snaps to whole weeks with a customizable startOfWeek in the range from minDate to maxDate
  • VisibleDateRange.weekAligned: displays visibleDayCount consecutive days while snapping to whole weeks with a customizable firstDay in the range from minDate to maxDate – can be used, e.g., to display a five-day workweek

3. Create a TimeController (optional)

Similar to the DateController above, a TimeController is also responsible for interacting with Timetable's widgets and managing their state. More specifically, it controls the visible time range and zoom factor in a MultiDateTimetable or RecurringMultiDateTimetable. You can also programmatically change those and, e.g., animate out to reveal the full day.

final myTimeController = TimeController(
  // All parameters are optional. By default, the whole day is revealed
  // initially and you can zoom in to view just a single minute.
  minDuration: 15.minutes, // The closest you can zoom in.
  maxDuration: 23.hours, // The farthest you can zoom out.
  initialRange: TimeRange(9.hours, 17.hours),
  maxRange: TimeRange(0.hours, 24.hours),
);

This example uses some of supercharged's extension methods on int to create a Duration more concisely.

Don't forget to dispose your controller, e.g., in State.dispose!

4. Create your Timetable widget

The configuration for Timetable's widgets is provided via inherited widgets. You can use a TimetableConfig<E> to provide all at once:

TimetableConfig<BasicEvent>(
  // Required:
  dateController: _dateController,
  timeController: _timeController,
  eventBuilder: (context, event) => BasicEventWidget(event),
  child: MultiDateTimetable<BasicEvent>(),
  // Optional:
  eventProvider: (date) => someListOfEvents,
  allDayEventBuilder: (context, event, info) =>
      BasicAllDayEventWidget(event, info: info),
  callbacks: TimetableCallbacks(
    // onWeekTap, onDateTap, onDateBackgroundTap, onDateTimeBackgroundTap
  ),
  theme: TimetableThemeData(
    context,
    // startOfWeek: DateTime.monday,
    // See the "Theming" section below for more options.
  ),
)

And you're done πŸŽ‰

Theming

Timetable already supports light and dark themes out of the box, adapting to the ambient ThemeData. You can, however, customize the styles of almost all components by providing a custom TimetableThemeData.

To apply your own theme, specify it in the TimetableConfig<E> (or directly in a TimetableTheme):

TimetableConfig<BasicEvent>(
  theme: TimetableThemeData(
    context,
    startOfWeek: DateTime.monday,
    dateDividersStyle: DateDividersStyle(
      context,
      color: Colors.blue.withOpacity(.3),
      width: 2,
    ),
    dateHeaderStyleProvider: (date) =>
        DateHeaderStyle(context, date, tooltip: 'My custom tooltip'),
    nowIndicatorStyle: NowIndicatorStyle(
      context,
      lineColor: Colors.green,
      shape: TriangleNowIndicatorShape(color: Colors.green),
    ),
    // See the "Theming" section below for more.
  ),
  // Other properties...
)

TimetableThemeData and all component styles provide two constructors each:

  • The default constructor takes a BuildContext and sometimes a day or month, using information from the ambient theme and locale to generate default values. You can still override all options via optional, named parameters.
  • The named raw constructor is usually const and has required parameters for all options.

Advanced Features

Drag and Drop

Drag and Drop demo

You can easily make events inside the content area of MultiDateTimetable or RecurringMultiDateTimetable draggable by wrapping them in a PartDayDraggableEvent:

PartDayDraggableEvent(
  // The user started dragging this event.
  onDragStart: () {},
  // The event was dragged to the given [DateTime].
  onDragUpdate: (dateTime) {},
  // The user finished dragging the event and landed on the given [DateTime].
  onDragEnd: (dateTime) {},
  child: MyEventWidget(),
  // By default, the child is displayed with a reduced opacity when it's
  // dragged. But, of course, you can customize this:
  childWhileDragging: OptionalChildWhileDragging(),
)

Timetable doesn't automatically show a moving feedback widget at the current pointer position. Instead, you can customize this and, e.g., snap to multiples of 15 minutes. Have a look at the included example app where we implemented exactly that by displaying the drag feedback as a time overlay.

Time Overlays

Drag and Drop demo

In addition to displaying events, MultiDateTimetable and RecurringMultiDateTimetable can display overlays for time ranges on every day. In the screenshot above, a light gray overlay is displayed on weekdays before 8β€―a.m. and after 8β€―p.m., and over the full day for weekends. Time overlays are provided similarly to events: Just add a timeOverlayProvider to your TimetableConfig<E> (or use a DefaultTimeOverlayProvider directly).

TimetableConfig<MyEvent>(
  timeOverlayProvider: (context, date) => <TimeOverlay>[
    TimeOverlay(
      start: 0.hours,
      end: 8.hours,
      widget: ColoredBox(color: Colors.black12),
      position: TimeOverlayPosition.behindEvents, // the default, alternatively `inFrontOfEvents`
    ),
    TimeOverlay(
      start: 20.hours,
      end: 24.hours,
      widget: ColoredBox(color: Colors.black12),
    ),
  ],
  // Other properties...
)

The provider is just a function that receives a date and returns a list of TimeOverlay for that date. The example above therefore draws a light gray background before 8β€―a.m. and after 8β€―p.m. on every day.

GitHub

https://github.com/JonasWanke/timetable
Comments
  • 1. feat: Available dates range support

    Closes: #25, #38

    Features

    • ability to define min and max dates
    • ability to define swipeRange for a VisibleRange.days
    • added jumpTo, jumpToToday, jumpToInitialDate, animateToInitialDate methods to controller

    Features for discussion

    • support of min/max date for WeekVisibleRange

    Checklist

    • [ ] remove redundant code
    • [ ] fix lint issues
    • [ ] update existing tests
    • [ ] update example App
    • [ ] Tests for the changes have been added (for bug fixes / features)
    • [ ] Docs have been added / updated (for bug fixes / features)
    Reviewed by TatsuUkraine at 2020-07-14 10:49
  • 2. Change color of currently selected day in CompactMonthTimetable

    How can currently selected date in CompactMonthTimetable be changed?

    In a way so that today date is always selected in its own colour (as it is now which is good), but for those manually selected dates in CompactMonthTimetable to set some custom color?

    image

    Reviewed by ghost at 2021-08-16 15:20
  • 3. Date controller contains wrong selected date

    Describe the bug Story

    • open example project
    • switch to 3 days view
    • scroll to state when Today (sometimes day after today) is a first one in 3 days list
    • swipe less than 40% width of the day column in the past
    • remove finger so time table return back to original date

    Expected behavior Date controller emits (has) date that 3 days interval start with

    Actual behavior Date controller has date that is one day before the actual one

    Screenshots

    https://user-images.githubusercontent.com/621542/121596282-ea75a780-ca47-11eb-876c-8e67e2fd84d6.mov

    Environment:

    • Device: Android Pixel 3 emulator/Android pixel 5 real device
    • OS version: Android 11
    • Package version: 1.0.0-alpha.1
    Reviewed by TatsuUkraine at 2021-06-10 21:00
  • 4. TimetableController's scrollController does not update its page value

    Description

    Scrolling on the calendar doesn't seem to change the value of the current page. I added the following listener:

    timetableController.scrollControllers.addPageChangedListener(() {
          print('Page changed to ${timetableController.scrollControllers.page}');
        });
    

    This causes it to print a message every time I try to scroll, but the value of the page printed always remains the same.

    As a side-effect of this, other listeners that depend on the page value, such as TimetableController.dateListenable and TimetableController.currentlyVisibleDatesListenable are also triggering their listeners, but their values aren't changing, and are remaining the same as the initialDate and initialTimeRange values.

    Screenshots

    image

    Environment:

    • Device: Huawei Honor 9 Lite
    • OS version: Android 9.0.0
    • Package version: 0.2.3
    Reviewed by RedMarbles at 2020-06-19 15:59
  • 5. Hide and disable week indicator

    How can week indicator be removed/hidden and disabled? image

    There is option to change styling of week indicator, but what about removing it?

    weekIndicatorStyleProvider: (week) => 
        WeekIndicatorStyle(context, week, labels: , textStyle: , tooltip: , padding: , decoration: ),
    
    Reviewed by ghost at 2021-08-13 10:36
  • 6. Add BoxDecoration theme to header container and hours container (left side)

    Add the ability to define BoxDecoration for the header container and hours container.

    It will be useful to keep the timetable up to date with any custom theme (like shadows aka Material elevation, shapes, borders and etc.) that dev is using in his App.

    P.S. Sorry that I'm spamming your package with issues)) But everything that I create is something that I need in my current task)

    Reviewed by TatsuUkraine at 2020-07-14 12:51
  • 7. Main layout repaints each second

    Describe the bug When timetable widget is initialized it starts to repaint entire table each second

    Screenshots timetable-repaint

    Environment:

    • Package version: 1.0.0-alpha.0
    Reviewed by TatsuUkraine at 2021-06-09 15:34
  • 8. Custom hour of day label formatter

    These changes add the features to custom the hour column. Currently, we can only set the TextStyle with the hour format of HH:mm and column width set at 48.0. This update adds the ability to set custom time formatting and column width.

    • [x] Adds theme property for formatting the hour of the day label.
    • [x] Adds theme property to set the width of hour column

    Example

    final theme = TimetableThemeData(
      formatHour: (time) => '${time.hourOf12HourClock} ${time.hourOfDay > 11 ? "PM" : "AM"}',
      hourColumnWidth: 40,
    );
    

    Checklist

    • [x] Tests for the changes have been added (for bug fixes / features)
    • [x] Docs have been added / updated (for bug fixes / features)

    Screenshots

    Format hour

    | Android | iOS | | :------------: | :------------: | | Screen Shot 2020-07-19 at 4 16 10 PM | Screen Shot 2020-07-19 at 4 19 12 PM |

    Set column width

    | Larger | Zero width | | :------------: | :------------: | | Screen Shot 2020-07-20 at 9 56 19 AM | Screen Shot 2020-07-20 at 9 56 32 AM|

    Reviewed by kennethj at 2020-07-19 21:19
  • 9. Tap on Backgrounds for DateEvents and AllDayEvents

    Closes: #18

    I thought I'd issue a pull request at the current state regarding #18.

    Background taps are currently recognized across DateEvents and AllDayEvents. Tapping on headers is currently not implemented yet. The pull request adapted the example with a snackbar function.

    There are some things on my mind and I'm looking forward to your feedback:

    1. Is the name onCreateEvent and onCreateAllDayEvent really appliceable? Maybe we should change it to somehting like onBackgroundTap or onCellTap?

    2. Regarding DateEvents I'm not quite sure why when calculating the position of the tapped cell I need to add a +1 here:

    final dateAndTime = DateTime(date.year, date.monthOfYear, date.dayOfYear, tappedCell.toInt() + 1);
    
    1. For allday events I even had to do +2 for the day.
    final dateAndTime = DateTime(date.year, date.monthOfYear, date.dayOfYear + 2);
    

    I'm not really sure about the calculations at all. The offset issue with the days might be related to wrong rounding? Might also be a conversion problem related to final startTime = LocalDateTime.dateTime(dateAndTime) I think? Maybe you could shed some light into this and the calculation at all. Also the time of the giving back object is always 23:00:00 eventhough it should be 00:00:00

    1. I think there are still some parts where I could make the code partly more elegant. Advices are appreciated :) !
    Reviewed by raLaaaa at 2020-06-21 13:02
  • 10. Listener when tapping the background (e.g. for creating an event)

    Hello there,

    any ideas on when this will be live? Are you open for foreign contributions?

    I could not find a contribution guide but I'd be happy to start working on this one since I could need it for a personal project.

    Just wanted to check in whether you are basically open for foreign contributions etc. and what a foreign contributor should consider?

    Would be a pity if I start working on this one and it's nearly done on your side or you don't want anyone to contribute to this project.

    Reviewed by raLaaaa at 2020-06-17 07:50
  • 11. Support Equatable

    This PR mainly adds support for equatable in Events. Support for it could be added in various other places as well, but this fixes an important bug in my application: Some users get the error message Events may not contain duplicates., which is not entirely correct in my case. The events I was adding are different in regards to the title and so on, but their start and end entries match which are the only entries that are being compared in timetable. By using the equatable package, one can simply compare additional entries in their custom Events by adding the following override:

      @override
      List<Object> get props => super.props..addAll([fieldToCompare1, fieldToCompare2]);
    

    It might be worth to consider such a widely-utilized package in this case. Thus, this PR adds compatability for it (in this particular case! There could be many other cases where it might make sense to migrate to it as well).

    • Updated dependencies and Android SDK entries
    • Migrated to Flutter embedding v2 (removes the warning when running the example)
    • Add support for equatable in events
    Reviewed by ThexXTURBOXx at 2022-04-14 14:42
  • 12. Vertical scrolling while dragging the event

    using the dateController.animateToPage is smooth in scrolling horizontally while dragging the event but using the timeController.animateTo to scroll vertically has a slow and weird behavior while dragging ... any help, please ?

    Reviewed by HaneenAhmedMohy at 2022-08-07 15:20
  • 13. isValidTimetableDateTime should provide a more precise assertion error

    During development one could encounter having a date with is an invalid time table date. I think that an date is none UTC conform is probably the most likely reason. But it could also be null. Asserting the value at various points with isValidTimetableDateTime is a great way to ensure this does not happen during development how ever if this happens the developer can not be certain for what reason it is not since there is no specific assertion message attached.

    I took a look at the code and I'm not quite sure if this is an easy fix due to the nature of assertions nor am I quite sure if this is really something the library should do for the developer. Hinting him to the assertion error(s) quite does the task.

    How ever it would be quite convenient when you believe you are searching for null values in your code but the date is simply UTC formatted.

    What do you think?

    Reviewed by raLaaaa at 2022-08-07 09:16
  • 14. Expose Offset to DateTime conversion

    At the moment the functionality to convert a Offset to the appropriate DateTime when dragging Events (using PartDayDraggableEvent) is not exposed, _DragInfos is private. It would be beneficial to expose this functionality, to allow for a better customization and handling custom drag'n'drop

    Possible Solution: It would be enough to just expose the _DragInfos class, by doing so, you could just call DragInfos.resolveOffset(...) inside of a custom Draggable. Also it would be great, to have the possibility to specify, which DragInfos instance should be used to be able to call this functionality from another subtree.

    Reviewed by TruFelix at 2022-07-02 18:22
  • 15. WeekIndicator Width changes on Screen Change to the Timetable

    When I am switching to the timetable screen, the width of the WeekIndicator changes. The change happens once, every time I change to this screen. It goes from full WeekIndicator to short.

    Example: Week 11, 2022 -> W11

    This is how I am using the MultiDateTimetable

    Widget build(BuildContext context) { return TimetableConfig<BasicEvent>( dateController: _dateController, timeController: _timeController, eventBuilder: (context, event) => BasicEventWidget(event), child: MultiDateTimetable<BasicEvent>(), ); }

    This is a part of a stateful widget. This is wrapped by a Scaffold which is then wrapped by a SafeArea.

    See the gif:

    ezgif com-gif-maker

    Reviewed by georgiossalon at 2022-03-16 22:35
  • 16. Detect when page in MultiDateTimetable completes

    How to detect when page completely changes? For example to get a callback not after you just begin with swiping, nor when you release a press while swiping, but when animation of page swipe ends? If you were to start swiping form one page to another and while on the middle between two pages you release a press, page will continue with animation to second page, and when it finishes animation i would like to get a callback.

    Reason why I'm asking is because I would like to fetch events from server whenever user swipes to new page, and issue with _dateController.addListener() is because it triggers a callback somewhat good when you swipe to next page, but when you try to swipe to previous page listener is triggered almost instantly as you being to swipe to previous page.

    I though to use NotificationListener, and it does work properly for horizontal swiping but issues with this approach is that it also detects when scrolling is active vertically, what i need is only to listen when horizontal page swiping ends

    void handleScrollListener(ScrollNotification scrollNotification) {
      if (scrollNotification is ScrollStartNotification) {
        print("SCROLLING");
      } else if (scrollNotification is ScrollEndNotification) {
        print("STOPPED SCROLLING!");
      }
    }
    
    @override
    Widget build(BuildContext context) {
      return NotificationListener<ScrollNotification>(
        onNotification: (scrollNotification) {
          SchedulerBinding.instance!.addPostFrameCallback(
              (_) => handleScrollListener(scrollNotification));
          return true;
        },
        child: Stack(
          children: [
            TimetableConfig<EventModel>(
    
    Reviewed by ghost at 2021-08-30 13:59

Related

A super powerful widget to help developers build complex views quickly and comfortably.
A super powerful widget to help developers build complex views quickly and comfortably.

FSuper FSuper can help developers build complex views quickly and comfortably. It supports rich text, rounded corners, borders, pictures, small red do

Aug 1, 2022
A super powerful widget to help developers build complex views quickly and comfortably.
A super powerful widget to help developers build complex views quickly and comfortably.

FSuper FSuper can help developers build complex views quickly and comfortably. It supports rich text, rounded corners, borders, pictures, small red do

Aug 1, 2022
The flutter_calendar_widget is highly customizable calendar widget.
The flutter_calendar_widget is highly customizable calendar widget.

flutter_calendar_widget The flutter_calendar_widget is highly customizable calendar widget. Not only can you change the style, but you can also change

Jun 24, 2022
Admob Flutter plugin that shows banner ads using native platform views.
Admob Flutter plugin that shows banner ads using native platform views.

Looking for Maintainers. Unfortunately I haven't been able to keep up with demand for features and improvments. If you are interested in helping maint

Aug 7, 2022
A flutter appliction listing all the widgets covered in Flutter widget of the week playlist.
A flutter appliction listing all the widgets covered in Flutter widget of the week playlist.

Flutter Widget Guide A flutter appliction listing all the widgets covered in Flutter widget of the week playlist Get the app Now featured on itsallwid

Aug 6, 2022
Custom calendar dialog widget for flutter with (multi select, single select, date range) mode
Custom calendar dialog widget for flutter with (multi select, single select, date range) mode

some calendar Custom calendar with Multi-select & range configurable calendar New Features Added View Mode Somecalendar #15 Help Maintenance I've take

Mar 30, 2022
Flutter mobile app with firestore authentication including Email and Social auth.
Flutter mobile app with firestore authentication including Email and Social auth.

Flutter mobile app with firestore authentication including Email and Social auth.

May 18, 2022
A port of kotlin-stdlib for Dart/Flutter including immutable collections (KtList, KtMap, KtSet) and other packages
A port of kotlin-stdlib for Dart/Flutter including immutable collections (KtList, KtMap, KtSet) and other packages

kt.dart This project is a port of Kotlin's Kotlin Standard library for Dart/Flutter projects. It's a useful addition to dart:core and includes collect

Aug 2, 2022
CARP Mobile Sensing for Flutter, including mobile sensing framework, data backend support, and the CARP mobile sensing app.
CARP Mobile Sensing for Flutter, including mobile sensing framework, data backend support, and the CARP mobile sensing app.

This repo hold the source code for the CACHET Research Platform (CARP) Mobile Sensing (CAMS) Flutter software. It contains the source code for CACHET

Jun 21, 2022
A iOS like table view including section, row, section header and divider
A iOS like table view including section, row, section header and divider

flutter_section_table_view A iOS like table view including section, row, section header and divider Support both Android and iOS Support drop-down ref

Dec 24, 2021
An app to show everything bus related in Singapore, including arrival times and a directory
An app to show everything bus related in Singapore, including arrival times and a directory

NextBus SG An app to show everything bus related in Singapore, including bus arrival times and a directory, with extra features. ?? Gallery Click here

Aug 7, 2022
Boilerplate codes including Superbase settings for Flutter
Boilerplate codes including Superbase settings for Flutter

flutter_boilerplate_supabase Boilerplate codes including Superbase settings for Flutter. Getting Started You have to create the .env file. Rename the

Feb 7, 2022
Building a simple Flutter app for understanding the BLoC State Management including: Cubit, Managing Route & showSnackBar.

Building a simple Flutter app for understanding the BLoC State Management including: Cubit, Managing Route & showSnackBar.

Jul 11, 2022
Building a simple Flutter app * Switch Theme * for understanding the BLoC State Management including: Cubit Communications with StreamSubscription & Managing Route.

Building a simple Flutter app * Switch Theme * for understanding the BLoC State Management including: Cubit Communications with StreamSubscription & Managing Route.

Apr 15, 2022
Custom dropdown widget allows to add highly customizable widget in your projects with proper open and close animations and also comes with form required validation.
Custom dropdown widget allows to add highly customizable widget in your projects with proper open and close animations and also comes with form required validation.

Custom Dropdown Custom Dropdown package lets you add customizable animated dropdown widget. Features Lots of properties to use and customize dropdown

Aug 8, 2022
A customizable carousel slider widget in Flutter which supports inifinte scrolling, auto scrolling, custom child widget, custom animations and built-in indicators.
A customizable carousel slider widget in Flutter which supports inifinte scrolling, auto scrolling, custom child widget, custom animations and built-in indicators.

flutter_carousel_widget A customizable carousel slider widget in Flutter. Features Infinite Scroll Custom Child Widget Auto Play Horizontal and Vertic

Jun 12, 2022
Control your week, days, hours, and even minutes. ⏳
Control your week, days, hours, and even minutes. ⏳

Info An advanced, minimalist, and powerful time management application. Where you can create the task, give it a duration, and select which weekdays y

Jul 11, 2022
Flutter-Week-Plans - Make Flutter Weekly plans with workout plans acc. to your choice.
Flutter-Week-Plans - Make Flutter Weekly plans with workout plans acc. to your choice.

Getting Started Flutter-Plans in a Workout Gym App with weekly added favourite plans. Images and Gif are taken randomly from Internet and are not arra

Jan 3, 2022
bootcamp flutter beginner Kuldi Project week 2

tugasweek2 A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Nov 27, 2021