Build a community-driven event calendar using a Custom Widget with the table_calendar package showing event dots on dates, a Firestore events collection with admin approval workflow, and RSVP tracking via an attendees subcollection. Users submit events through a creation form, admins approve them via a status toggle, and community members browse by date and RSVP. Events are queried by visible month range to keep the calendar responsive even with thousands of entries.
Building a Community Event Calendar in FlutterFlow
A dynamic event calendar lets your community discover and share events. This tutorial builds the full system: a monthly calendar view with event dots, user-submitted events with admin approval, RSVP tracking, and optimized querying by date range. It works for community apps, local business directories, school portals, or any platform where users contribute events.
Prerequisites
- A FlutterFlow project on the Pro plan (Custom Widget required)
- Firebase project with Firestore and Authentication enabled
- Basic familiarity with FlutterFlow Custom Widgets and Action Flows
- The table_calendar package added to Pubspec Dependencies
Step-by-step guide
Set up the events collection and approval schema
Set up the events collection and approval schema
Create a Firestore events collection with fields: title (String), description (String), date (Timestamp), startTime (String), endTime (String), location (String), createdBy (String, userId), category (String), attendeeCount (Integer, default 0), imageUrl (String), isApproved (Boolean, default false), and status (String: 'pending', 'approved', 'rejected'). Under each event, create an attendees subcollection with documents keyed by userId containing rsvpAt (Timestamp). The isApproved flag ensures only admin-vetted events appear on the public calendar.
Expected result: Firestore has an events collection with approval workflow fields and an attendees subcollection for RSVP tracking.
Create the calendar Custom Widget with table_calendar
Create the calendar Custom Widget with table_calendar
Add table_calendar: ^3.0.0 to Pubspec Dependencies. Create a Custom Widget called EventCalendar that accepts a Component Parameter eventDates (List of DateTime) and an Action Parameter callback onDaySelected(DateTime). In the build method, return a TableCalendar with firstDay set to 2 years ago, lastDay to 2 years from now, and focusedDay to today. Implement the eventLoader callback: for each day, count how many dates in eventDates match that day and return a list of that length. The calendar renders a dot per event on each day. On day tap, call widget.onDaySelected with the tapped date.
1// Custom Widget: EventCalendar2import 'package:flutter/material.dart';3import 'package:table_calendar/table_calendar.dart';45class EventCalendarWidget extends StatefulWidget {6 final double width;7 final double height;8 final List<DateTime> eventDates;9 final Future Function(DateTime selectedDay)? onDaySelected;1011 const EventCalendarWidget({12 Key? key,13 required this.width,14 required this.height,15 required this.eventDates,16 this.onDaySelected,17 }) : super(key: key);1819 @override20 State<EventCalendarWidget> createState() => _EventCalendarWidgetState();21}2223class _EventCalendarWidgetState extends State<EventCalendarWidget> {24 DateTime _focusedDay = DateTime.now();25 DateTime? _selectedDay;2627 @override28 Widget build(BuildContext context) {29 return SizedBox(30 width: widget.width,31 height: widget.height,32 child: TableCalendar(33 firstDay: DateTime.now().subtract(const Duration(days: 730)),34 lastDay: DateTime.now().add(const Duration(days: 730)),35 focusedDay: _focusedDay,36 selectedDayPredicate: (day) => isSameDay(_selectedDay, day),37 eventLoader: (day) {38 return widget.eventDates39 .where((d) => isSameDay(d, day))40 .toList();41 },42 onDaySelected: (selected, focused) {43 setState(() {44 _selectedDay = selected;45 _focusedDay = focused;46 });47 widget.onDaySelected?.call(selected);48 },49 calendarStyle: const CalendarStyle(50 markerDecoration: BoxDecoration(51 color: Colors.blue,52 shape: BoxShape.circle,53 ),54 todayDecoration: BoxDecoration(55 color: Colors.blueAccent,56 shape: BoxShape.circle,57 ),58 selectedDecoration: BoxDecoration(59 color: Colors.deepPurple,60 shape: BoxShape.circle,61 ),62 ),63 ),64 );65 }66}Expected result: The calendar renders with blue dots on dates that have events. Tapping a day triggers the onDaySelected callback.
Build the day detail view with event list and RSVP
Build the day detail view with event list and RSVP
When onDaySelected fires, store the selected date in Page State selectedDate. Below the calendar, add a ListView with a Backend Query filtering events where date falls within the selected day (startOfDay <= date < endOfDay) AND isApproved == true. Each event card shows the title, time, location, category badge, attendeeCount, and an RSVP button. The RSVP button checks if the current user exists in the event's attendees subcollection. If not, tapping creates an attendee document and increments attendeeCount atomically. If already RSVPed, show 'Going' with an option to cancel.
Expected result: Tapping a day shows a list of approved events for that date. Each event has an RSVP button that toggles between 'RSVP' and 'Going' states.
Create the event submission form with approval workflow
Create the event submission form with approval workflow
Create a CreateEvent page with a form containing: title TextField, description TextField (multiline), DateTimePicker for the event date, start and end time TextFields, location TextField, category DropDown (from an Option Set), and an optional FlutterFlowUploadButton for an event image. On submit, create an event document with status set to 'pending' and isApproved set to false. Show a SnackBar confirming the submission is pending review. Create an AdminEvents page showing a ListView of pending events with Approve and Reject buttons that update the status and isApproved fields.
Expected result: Users submit events through the form and see a pending confirmation. Admins review and approve or reject events from the admin page.
Optimize calendar queries by visible month range
Optimize calendar queries by visible month range
Instead of loading all events at once, query only the visible month's events. On the calendar Custom Widget, add an onPageChanged callback parameter that fires when the user swipes to a new month, returning the new focused month. In FlutterFlow, when onPageChanged fires, update Page State currentMonth and re-run the Backend Query with filters: date >= firstDayOfMonth AND date <= lastDayOfMonth AND isApproved == true. Pass the resulting dates list to the calendar widget's eventDates parameter. This keeps reads proportional to events per month, not total events.
Expected result: The calendar only loads events for the visible month. Swiping to a new month triggers a new query, keeping performance fast even with thousands of total events.
Add social sharing for events
Add social sharing for events
On the event detail view, add a Share IconButton. Create a Custom Action using the share_plus package that composes a share message: 'Check out {title} on {date} at {location}! RSVP here: {deepLinkUrl}'. Call Share.share(message) to open the native share sheet. For the deep link, use a URL that opens your app's event detail page (e.g., yourapp.com/event/{eventId}). This drives organic discovery of your community calendar.
Expected result: Tapping the share button opens the native share sheet with event details and a link. Recipients can tap the link to view the event and RSVP.
Complete working example
1// Custom Widget: EventCalendar with Month Change Callback2import 'package:flutter/material.dart';3import 'package:table_calendar/table_calendar.dart';45class EventCalendarWidget extends StatefulWidget {6 final double width;7 final double height;8 final List<DateTime> eventDates;9 final Future Function(DateTime selectedDay)? onDaySelected;10 final Future Function(DateTime focusedMonth)? onPageChanged;1112 const EventCalendarWidget({13 Key? key,14 required this.width,15 required this.height,16 required this.eventDates,17 this.onDaySelected,18 this.onPageChanged,19 }) : super(key: key);2021 @override22 State<EventCalendarWidget> createState() => _EventCalendarWidgetState();23}2425class _EventCalendarWidgetState extends State<EventCalendarWidget> {26 DateTime _focusedDay = DateTime.now();27 DateTime? _selectedDay;2829 @override30 Widget build(BuildContext context) {31 return SizedBox(32 width: widget.width,33 height: widget.height,34 child: TableCalendar(35 firstDay: DateTime.now().subtract(const Duration(days: 730)),36 lastDay: DateTime.now().add(const Duration(days: 730)),37 focusedDay: _focusedDay,38 selectedDayPredicate: (day) => isSameDay(_selectedDay, day),39 eventLoader: (day) {40 return widget.eventDates.where((d) => isSameDay(d, day)).toList();41 },42 onDaySelected: (selected, focused) {43 setState(() {44 _selectedDay = selected;45 _focusedDay = focused;46 });47 widget.onDaySelected?.call(selected);48 },49 onPageChanged: (focusedDay) {50 setState(() => _focusedDay = focusedDay);51 widget.onPageChanged?.call(focusedDay);52 },53 calendarStyle: CalendarStyle(54 markerDecoration: BoxDecoration(55 color: Theme.of(context).primaryColor,56 shape: BoxShape.circle,57 ),58 markerSize: 6.0,59 markersMaxCount: 3,60 todayDecoration: BoxDecoration(61 color: Theme.of(context).primaryColor.withOpacity(0.5),62 shape: BoxShape.circle,63 ),64 selectedDecoration: BoxDecoration(65 color: Theme.of(context).primaryColor,66 shape: BoxShape.circle,67 ),68 ),69 headerStyle: const HeaderStyle(70 formatButtonVisible: false,71 titleCentered: true,72 ),73 ),74 );75 }76}Common mistakes when creating a Dynamic Event Calendar with User Content in FlutterFlow
Why it's a problem: Loading all events for the entire year when the calendar renders
How to avoid: Query events by the visible month range only (date >= monthStart AND date <= monthEnd) and re-query when the user navigates to a different month.
Why it's a problem: Showing unapproved events on the public calendar
How to avoid: Default new events to isApproved: false and filter all public calendar queries with isApproved == true. Only admins can set isApproved to true.
Why it's a problem: Incrementing attendeeCount on the client without a transaction
How to avoid: Use FieldValue.increment(1) in the update action or a Firestore transaction to atomically increment the attendee count.
Best practices
- Query events by visible month range and re-query on month navigation for scalable performance
- Use event dot markers (not text) on the calendar to keep the UI clean and scannable
- Limit event dots to a maximum of 3 per day to avoid visual clutter
- Add category-based color coding to event dots for quick visual scanning
- Pre-create composite Firestore indexes on date + isApproved + category for filtered queries
- Send push notifications to RSVPed users 24 hours before the event as a reminder
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to build a community event calendar in FlutterFlow using the table_calendar package. Show me the Custom Widget code with event dots, the Firestore schema for events with admin approval, and the RSVP system with attendee subcollections.
Create an event calendar page with a table_calendar Custom Widget showing dots on dates with events. Below the calendar, show a list of events for the selected day with RSVP buttons. Add a form for users to submit new events that require admin approval.
Frequently asked questions
Can I show recurring events on the calendar?
Yes. Add a recurrence field (daily, weekly, monthly) to the event document and a Custom Function that generates recurring dates from the start date. Pass these generated dates to the calendar's eventDates list.
How do I handle events that span multiple days?
Store both startDate and endDate on the event. In the eventLoader, check if the calendar day falls between startDate and endDate inclusive. The dot will appear on every day of the event.
Can users filter events by category on the calendar?
Yes. Add ChoiceChips above the calendar for categories. When a category is selected, re-query events with the category filter and update the eventDates list passed to the Custom Widget.
How do I limit the number of events a single user can submit?
Add a Firestore query in the event creation flow that counts events where createdBy matches the current user and status is not rejected. If the count exceeds your limit, show an error and block submission.
Can I export calendar events to Google Calendar or Apple Calendar?
Yes. Generate an .ics file string from the event data using a Custom Function and use a Launch URL action with a data URI or a download link to let users add the event to their native calendar.
Can RapidDev help build a full community platform with an event calendar?
Yes. RapidDev can implement a complete community platform with event management, ticketing, venue maps, automated reminders, and analytics on event engagement.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation