Build a standalone calendar Component using the table_calendar Custom Widget with three view modes: month, two-week, and week. Query events by the visible date range and map them to calendar days as colored dots. Tapping a day opens a Bottom Sheet listing that day's events. Swipe left and right to navigate months. Style the calendar with FlutterFlowTheme colors for the header, selected day, today highlight, and event dots. Accept events as a Component Parameter for flexible embedding across different pages.
Building a Reusable Calendar View Component in FlutterFlow
A calendar view is a foundational component for booking apps, event platforms, scheduling tools, and personal productivity apps. This tutorial builds a standalone calendar Component using the table_calendar package that supports month, two-week, and week views, displays event dots, and opens a detail sheet on day tap. It is designed as a reusable Component you can embed on any page.
Prerequisites
- A FlutterFlow project with Firestore configured
- The table_calendar package added as a Custom Widget dependency
- An events or similar collection in Firestore with date fields
- Basic familiarity with FlutterFlow Custom Widgets and Component Parameters
Step-by-step guide
Set up the Custom Widget with table_calendar
Set up the Custom Widget with table_calendar
Create a Custom Widget CalendarView that imports the table_calendar package. Accept Component Parameters: events (List of Maps with 'date' DateTime and 'title' String), onDaySelected (callback with DateTime), initialFormat (String: 'month', 'twoWeeks', 'week'). Inside the widget, create a TableCalendar instance with firstDay set to one year ago, lastDay set to one year from now, and focusedDay set to today. Map the CalendarFormat enum based on initialFormat. Use a StatefulWidget to track focusedDay, selectedDay, and calendarFormat in local state.
1// Custom Widget: CalendarView2import 'package:table_calendar/table_calendar.dart';34class CalendarView extends StatefulWidget {5 final List<Map<String, dynamic>> events;6 final Function(DateTime) onDaySelected;7 const CalendarView({8 required this.events,9 required this.onDaySelected,10 });1112 @override13 State<CalendarView> createState() => _CalendarViewState();14}1516class _CalendarViewState extends State<CalendarView> {17 CalendarFormat _format = CalendarFormat.month;18 DateTime _focused = DateTime.now();19 DateTime? _selected;2021 List<Map<String, dynamic>> _getEventsForDay(DateTime day) {22 return widget.events.where((e) {23 final d = e['date'] as DateTime;24 return d.year == day.year &&25 d.month == day.month &&26 d.day == day.day;27 }).toList();28 }2930 @override31 Widget build(BuildContext context) {32 return TableCalendar(33 firstDay: DateTime.now().subtract(Duration(days: 365)),34 lastDay: DateTime.now().add(Duration(days: 365)),35 focusedDay: _focused,36 calendarFormat: _format,37 selectedDayPredicate: (d) => isSameDay(_selected, d),38 onDaySelected: (selected, focused) {39 setState(() {40 _selected = selected;41 _focused = focused;42 });43 widget.onDaySelected(selected);44 },45 onFormatChanged: (f) => setState(() => _format = f),46 onPageChanged: (f) => setState(() => _focused = f),47 eventLoader: _getEventsForDay,48 );49 }50}Expected result: A Custom Widget renders the table_calendar with month view, navigation, and event loading.
Style the calendar with FlutterFlowTheme colors
Style the calendar with FlutterFlowTheme colors
Add CalendarStyle and HeaderStyle properties to the TableCalendar. Set todayDecoration to a CircleDecoration with FlutterFlowTheme primaryColor at 30% opacity. Set selectedDecoration to a CircleDecoration with solid primaryColor. Set markerDecoration (event dots) to small CircleDecoration with secondary color. For the header, set formatButtonDecoration with a rounded border, titleTextStyle with headlineSmall, and left/right arrow colors matching the theme. Set weekendTextStyle to use a muted color. Set outsideDaysVisible to false to keep the calendar clean. These styles ensure the calendar matches your app's design system.
Expected result: The calendar is styled with your app's theme colors for a cohesive look.
Query events by visible date range from Firestore
Query events by visible date range from Firestore
On the page embedding the CalendarView Component, create a Backend Query on your events collection. Filter where date >= first day of current month AND date <= last day of current month. Pass the query results to the CalendarView Component Parameter events as a list of Maps. When the user navigates to a different month (onPageChanged callback), update the query date range. To minimize re-queries, load events for the current month plus one month before and after (three-month window). Store the loaded events in Page State and only re-query when the user navigates outside the loaded range.
Expected result: Events for the visible month load from Firestore and display as dots on the calendar.
Display event dots on calendar days
Display event dots on calendar days
The eventLoader property on TableCalendar returns events for each day. The calendar automatically renders marker dots below each day that has events. Customize the markers: use calendarBuilders.markerBuilder to create custom dot widgets. Show 1 dot for 1 event, 2 dots for 2 events, and 3 dots with a '+' for more than 3 events. Color-code dots by event category: blue for meetings, green for personal, red for deadlines. Create a Row of small Container circles (8x8 pixels) with the appropriate colors. Limit to 3 visible dots per day to keep the calendar clean.
Expected result: Calendar days show color-coded event dots indicating the number and type of events.
Show day events in a Bottom Sheet on tap
Show day events in a Bottom Sheet on tap
When the user taps a calendar day, the onDaySelected callback fires. In FlutterFlow, connect this callback to show a Bottom Sheet DayEventsSheet Component. Pass the selected date and filtered events for that day as Component Parameters. Inside the Bottom Sheet, display: the date as a formatted heading Text, a ListView of events for that day (each showing: colored dot Container matching the event category, title Text, time Text, and a brief description). If no events exist for the tapped day, show an empty state with 'No events' Text and an 'Add Event' Button. Add a Today IconButton in the calendar header that resets focusedDay to DateTime.now().
Expected result: Tapping a calendar day opens a Bottom Sheet listing all events for that day with category colors.
Complete working example
1PACKAGE REQUIRED:2 table_calendar: ^3.0.034FIRESTORE DATA MODEL:5 events/{eventId}6 title: String7 description: String8 date: Timestamp9 endDate: Timestamp (optional)10 category: String11 userId: String1213CUSTOM WIDGET: CalendarView14 Component Parameters:15 events: List<Map> [{date, title, category}]16 onDaySelected: Callback(DateTime)1718 TableCalendar(19 firstDay: 1 year ago20 lastDay: 1 year from now21 focusedDay: state._focused22 calendarFormat: state._format (month/twoWeeks/week)23 selectedDayPredicate: isSameDay(_selected, day)24 onDaySelected: update _selected, call callback25 onFormatChanged: update _format26 onPageChanged: update _focused27 eventLoader: _getEventsForDay28 calendarStyle:29 todayDecoration: primary 30% opacity circle30 selectedDecoration: primary solid circle31 markerDecoration: secondary small circles32 headerStyle:33 formatButtonDecoration: rounded border34 titleTextStyle: headlineSmall35 )3637EVENT DOTS:38 calendarBuilders.markerBuilder:39 Row of up to 3 colored Container circles (8x8)40 Blue = meeting, Green = personal, Red = deadline41 More than 3: show 3 dots + text4243DAY EVENTS BOTTOM SHEET:44 Column45 ├── Text (formatted date, headlineMedium)46 ├── ListView (events for selected day)47 │ └── Row48 │ ├── Container (category dot, colored)49 │ ├── Column50 │ │ ├── Text (title)51 │ │ └── Text (time, small)52 │ └── Icon (chevron right)53 └── Empty State (if no events)54 Text ("No events")55 Button ("Add Event")5657QUERY STRATEGY:58 Load events for current month +/- 1 month59 Re-query on onPageChanged when outside loaded range60 Store loaded events in Page StateCommon mistakes when creating a Calendar View in FlutterFlow
Why it's a problem: Loading all events for the entire year in the initial query
How to avoid: Query only the visible month range (plus one month buffer on each side). Use the onPageChanged callback to load new events when the user navigates to a different month.
Why it's a problem: Not handling the onPageChanged callback to update the event query
How to avoid: Listen to onPageChanged, update the focusedDay state, and trigger a new Backend Query for the new visible date range. Update the events Component Parameter with the new results.
Why it's a problem: Using Conditional Visibility toggling instead of TableCalendar for the calendar grid
How to avoid: Use the table_calendar package as a Custom Widget. It handles all date math, layout, navigation, and accessibility out of the box.
Best practices
- Use the table_calendar package for reliable date handling and calendar layout
- Query events by visible date range only, not the entire collection
- Re-query events on month navigation using the onPageChanged callback
- Color-code event dots by category for quick visual scanning
- Limit visible dots to 3 per day to keep the calendar layout clean
- Style the calendar using FlutterFlowTheme colors for design consistency
- Accept events as a Component Parameter so the calendar can be reused on different pages with different data sources
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a calendar view in FlutterFlow using the table_calendar package. Show me how to create a Custom Widget with month/week/two-week views, load events from Firestore by visible date range, display color-coded event dots on calendar days, show a Bottom Sheet with day events on tap, and style everything with app theme colors.
Create a page with a calendar widget taking up the top half of the screen and a bottom sheet that slides up when a day is tapped, showing a list of events for that day.
Frequently asked questions
Can I add the ability to create events directly from the calendar?
Yes. When the user taps a day with no events, show an 'Add Event' button in the Bottom Sheet. Tapping it opens an event creation form pre-filled with the selected date. On save, create the event document in Firestore and refresh the calendar events.
How do I show multi-day events that span across several days?
Add a startDate and endDate to each event. In the eventLoader, check if each day falls within the start-end range of any event. Use calendarBuilders to draw a background highlight spanning the date range instead of individual dots.
Can I make the calendar start on Monday instead of Sunday?
Yes. Set the startingDayOfWeek property on TableCalendar to StartingDayOfWeek.monday. This shifts the entire grid so Monday appears in the first column.
How do I sync with Google Calendar or Apple Calendar?
Use the Google Calendar API or Apple EventKit via a Cloud Function. Fetch external calendar events and either import them into your Firestore events collection or merge them at display time in the eventLoader function.
Can I add drag-and-drop to reschedule events?
The table_calendar package does not support drag-and-drop natively. For event rescheduling, use a long-press gesture on an event in the Bottom Sheet list, then show a date picker to select the new date and update the Firestore document.
Can RapidDev help build an advanced calendar system?
Yes. RapidDev can implement a full calendar system with recurring events, drag-and-drop rescheduling, Google Calendar sync, time zone handling, availability management, and team calendar views.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation