Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Create an Event Planning and Guest Management App in FlutterFlow

Build your event management app with two Firestore collections: events and event_guests (as a separate collection, not an array on the event). Add RSVP tracking with status field (invited/confirmed/declined/checked_in), QR code check-in using mobile_scanner Custom Widget, email invitations via Cloud Function, and a live attendance dashboard using Firestore aggregate queries.

What you'll learn

  • How to structure events and event_guests as separate Firestore collections for scalability
  • How to build an RSVP system with status tracking and confirmation emails
  • How to implement QR code check-in using mobile_scanner Custom Widget
  • How to display a real-time attendance dashboard with counts by status
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read60-80 minFlutterFlow Free+ (Firebase required; Pro for QR scanner Custom Widget)March 2026RapidDev Engineering Team
TL;DR

Build your event management app with two Firestore collections: events and event_guests (as a separate collection, not an array on the event). Add RSVP tracking with status field (invited/confirmed/declined/checked_in), QR code check-in using mobile_scanner Custom Widget, email invitations via Cloud Function, and a live attendance dashboard using Firestore aggregate queries.

Event Management Built Right in Firestore

Event management apps look straightforward until you hit the Firestore 1MB document limit with a large guest list, or try to query all confirmed attendees across hundreds of events. The correct data model separates guests into their own collection from day one. This guide builds the full stack: event creation, invitation sending, RSVP tracking, QR code check-in at the door, and a live dashboard — all wired to Firestore real-time listeners so organizers see attendance update as guests arrive.

Prerequisites

  • FlutterFlow project with Firebase and Firestore configured
  • Firebase Authentication set up for organizer login
  • Cloud Functions enabled (Firebase Blaze plan) for email invitation sending
  • Basic understanding of Firestore collections and queries

Step-by-step guide

1

Design the Firestore Data Model for Events and Guests

Create two top-level Firestore collections. The 'events' collection stores: title, description, date_time, location, organizer_id, max_capacity, cover_image_url, is_public, and created_at. The 'event_guests' collection stores each invitation as a separate document with: event_id, guest_email, guest_name, guest_user_id (nullable for non-app users), status (String — 'invited', 'confirmed', 'declined', 'checked_in'), invited_at, responded_at, checked_in_at, and qr_code (a unique token for check-in). This separation means you can have 10,000 guests per event without hitting Firestore's document size limit, and you can query all events a specific guest has been invited to across your entire database.

Expected result: Firestore shows an 'events' collection and an 'event_guests' collection. Each event_guests document has a unique qr_code token field.

2

Build the Event Creation and RSVP Form

Create an EventCreationPage with text fields for title, description, and location, a DateTimePicker for the event date and time, an image upload for the cover photo, and a NumberField for max_capacity. On form submission, create the event document in Firestore and navigate to the event detail page. On the EventDetailPage, add an RSVP section with Confirm and Decline buttons. Tapping Confirm creates an event_guests document (if not already present) with status 'confirmed', or updates an existing invitation's status. Show the current RSVP counts (confirmed, declined, pending) in a Row of stat chips at the top of the page using a Firestore aggregate query or a count from a StreamBuilder.

Expected result: Organizers can create events and see them in a list. Guests can tap Confirm or Decline and see their status reflected immediately.

3

Send Email Invitations via Cloud Function

Create a Cloud Function called sendEventInvitations that accepts an event_id and a list of email addresses. For each email, check if the address already has an event_guests document for this event — skip if already invited. Create the event_guests document with status 'invited' and a generated UUID as the qr_code. Send an invitation email using SendGrid or Firebase's Email Trigger Extension with the event details, RSVP links, and a note that their QR code will be sent separately upon confirmation. In FlutterFlow, add an Invite Guests button on the EventDetailPage that opens a dialog with a multi-line TextField for comma-separated email addresses.

send_event_invitations.js
1const functions = require('firebase-functions');
2const admin = require('firebase-admin');
3const { v4: uuidv4 } = require('uuid');
4
5exports.sendEventInvitations = functions.https.onCall(async (data, context) => {
6 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');
7
8 const { eventId, emails } = data;
9 const db = admin.firestore();
10
11 const eventSnap = await db.collection('events').doc(eventId).get();
12 if (!eventSnap.exists) throw new functions.https.HttpsError('not-found', 'Event not found');
13
14 const event = eventSnap.data();
15 const results = { sent: 0, skipped: 0 };
16
17 for (const email of emails) {
18 const cleanEmail = email.trim().toLowerCase();
19 if (!cleanEmail) continue;
20
21 // Check for existing invitation
22 const existing = await db.collection('event_guests')
23 .where('event_id', '==', eventId)
24 .where('guest_email', '==', cleanEmail)
25 .limit(1).get();
26
27 if (!existing.empty) { results.skipped++; continue; }
28
29 const qrToken = uuidv4();
30 await db.collection('event_guests').add({
31 event_id: eventId,
32 guest_email: cleanEmail,
33 guest_name: cleanEmail.split('@')[0],
34 guest_user_id: null,
35 status: 'invited',
36 qr_code: qrToken,
37 invited_at: admin.firestore.FieldValue.serverTimestamp(),
38 responded_at: null,
39 checked_in_at: null,
40 });
41
42 // Send email via trigger extension or SendGrid
43 await db.collection('mail').add({
44 to: cleanEmail,
45 template: { name: 'event-invitation', data: { eventTitle: event.title, eventDate: event.date_time.toDate().toLocaleDateString(), qrToken } },
46 });
47
48 results.sent++;
49 }
50
51 return results;
52});

Expected result: Pasting 10 email addresses and tapping Send Invitations sends emails to all new invitees and shows a summary 'Sent: 8, Skipped: 2 (already invited)'.

4

Build the QR Code Check-In Scanner

Create a Custom Widget called QRScannerWidget using the mobile_scanner package. The widget shows the camera viewfinder and calls a callback with the scanned QR code string when detected. On the EventCheckinPage, display this widget and wire the scan callback to a Custom Action called processCheckin. The processCheckin action queries event_guests where qr_code equals the scanned value AND event_id equals the current event. If found and status is 'confirmed', update status to 'checked_in' and checked_in_at to server timestamp. Show a green success overlay with the guest name. If status is already 'checked_in', show a yellow warning 'Already checked in'. If not found, show a red error overlay.

qr_scanner_widget.dart
1import 'package:mobile_scanner/mobile_scanner.dart';
2import 'package:flutter/material.dart';
3
4class QRScannerWidget extends StatefulWidget {
5 final ValueChanged<String> onScanned;
6 const QRScannerWidget({super.key, required this.onScanned});
7
8 @override
9 State<QRScannerWidget> createState() => _QRScannerWidgetState();
10}
11
12class _QRScannerWidgetState extends State<QRScannerWidget> {
13 final MobileScannerController _controller = MobileScannerController();
14 bool _isProcessing = false;
15
16 @override
17 Widget build(BuildContext context) {
18 return MobileScanner(
19 controller: _controller,
20 onDetect: (capture) {
21 if (_isProcessing) return;
22 final barcode = capture.barcodes.firstOrNull;
23 final value = barcode?.rawValue;
24 if (value != null) {
25 setState(() => _isProcessing = true);
26 widget.onScanned(value);
27 // Reset after 2 seconds to allow next scan
28 Future.delayed(const Duration(seconds: 2), () {
29 if (mounted) setState(() => _isProcessing = false);
30 });
31 }
32 },
33 );
34 }
35
36 @override
37 void dispose() {
38 _controller.dispose();
39 super.dispose();
40 }
41}

Expected result: Pointing the camera at a guest's QR code shows their name in a green banner within 1 second. The check-in status updates in Firestore and the attendance count increments on the dashboard.

5

Build the Real-Time Attendance Dashboard

Create an AttendanceDashboardPage with stat cards showing: Total Invited (count of all event_guests for this event), Confirmed (count where status == 'confirmed'), Checked In (count where status == 'checked_in'), Declined (count where status == 'declined'), and a percentage capacity bar (checked_in / max_capacity). Use Firestore StreamBuilders for each count query. Add a ListView of recent check-ins ordered by checked_in_at descending showing the last 20 arrivals with guest name and time. Add a search TextField that filters the full guest list by name or email for organizers who need to manually look up a guest.

Expected result: As guests check in, the Checked In counter increments in real time. The capacity bar fills gradually. The Recent Arrivals list updates within seconds of each scan.

Complete working example

event_checkin_action.dart
1// FlutterFlow Custom Action: processCheckin
2// Called when QR scanner detects a code
3// Returns: { status: 'success'|'already_checked_in'|'not_found', guestName: String }
4
5import 'package:cloud_firestore/cloud_firestore.dart';
6
7Future<Map<String, dynamic>> processCheckin(
8 String qrCode,
9 String eventId,
10) async {
11 final db = FirebaseFirestore.instance;
12
13 // Find the guest document by QR code and event ID
14 final querySnap = await db
15 .collection('event_guests')
16 .where('qr_code', isEqualTo: qrCode)
17 .where('event_id', isEqualTo: eventId)
18 .limit(1)
19 .get();
20
21 if (querySnap.docs.isEmpty) {
22 return {'status': 'not_found', 'guest_name': ''};
23 }
24
25 final doc = querySnap.docs.first;
26 final data = doc.data();
27 final currentStatus = data['status'] as String? ?? '';
28
29 if (currentStatus == 'checked_in') {
30 return {
31 'status': 'already_checked_in',
32 'guest_name': data['guest_name'] ?? 'Guest',
33 'checked_in_at': data['checked_in_at'],
34 };
35 }
36
37 if (currentStatus == 'declined') {
38 return {
39 'status': 'declined',
40 'guest_name': data['guest_name'] ?? 'Guest',
41 };
42 }
43
44 // Update status to checked_in
45 await doc.reference.update({
46 'status': 'checked_in',
47 'checked_in_at': FieldValue.serverTimestamp(),
48 });
49
50 // Increment event checked_in counter
51 await db.collection('events').doc(eventId).update({
52 'checked_in_count': FieldValue.increment(1),
53 });
54
55 return {
56 'status': 'success',
57 'guest_name': data['guest_name'] ?? 'Guest',
58 'guest_email': data['guest_email'] ?? '',
59 };
60}

Common mistakes when creating an Event Planning and Guest Management App in FlutterFlow

Why it's a problem: Storing the guest list as an Array field on the event document

How to avoid: Store guests as a separate 'event_guests' collection with an event_id field. This scales to millions of guests and allows cross-event queries like 'show all events this user was invited to'.

Why it's a problem: Using a sequential integer as the QR code token

How to avoid: Always use a UUID or cryptographically random token as the QR code value. UUIDs are unguessable and unique across your entire database.

Why it's a problem: Not handling the already-checked-in case in the scanner

How to avoid: Check the current status before updating. If already 'checked_in', show a yellow warning with the original check-in timestamp instead of a success message, and do not increment the counter again.

Best practices

  • Add a counter field (confirmed_count, checked_in_count) directly on the event document and use FieldValue.increment() when statuses change — this avoids expensive aggregate queries for the dashboard.
  • Generate QR codes as UUIDs on the server (Cloud Function) not on the client to ensure uniqueness and prevent manipulation.
  • Add a waitlist feature: when confirmed_count reaches max_capacity, change RSVP flow to add to a waitlist status and promote when others decline.
  • Cache the event document in App State on the checkin page so the scanner does not need to re-fetch event details for every scan.
  • Add a torch toggle button to the QR scanner for dimly lit event venues — this dramatically improves scan reliability.
  • For paid events, add a payment_status field alongside the RSVP status and block check-in if payment is not confirmed.
  • Export attendance as CSV from a Cloud Function for post-event analytics — include guest name, email, RSVP response time, and check-in time.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building an event management app in FlutterFlow with Firebase. Show me the correct Firestore data model for events and guest lists at scale, how to send invitation emails via Cloud Function, how to implement QR code check-in using mobile_scanner in a Custom Widget, and how to build a real-time attendance dashboard. Include Dart and JavaScript code.

FlutterFlow Prompt

In my FlutterFlow app, create a Custom Action called processCheckin that takes a qrCode string and eventId string, queries the event_guests Firestore collection for the matching document, returns 'success' with the guest name if status is confirmed, returns 'already_checked_in' if status is already checked_in, and returns 'not_found' if no matching document exists. Update the status and checked_in_at field on success.

Frequently asked questions

How do I let attendees add events to their phone calendar?

Use the add_2_calendar Flutter package in a Custom Action. Create a CustomEvent object with the event title, description, location, start time, and end time, then call Add2Calendar.addEvent2Cal(event). On iOS, this opens the native Calendar add dialog. On Android, it opens the default calendar app. This requires no additional permissions — the user confirms by tapping Add in the calendar dialog.

Can guests RSVP without creating an account in my app?

Yes, using anonymous authentication. When a guest clicks the RSVP link in their invitation email, sign them in anonymously with Firebase Auth, link their email to the anonymous account, and create/update their event_guests document. You can later upgrade the anonymous account to a full email/password account when they create a profile. This allows frictionless RSVP without requiring sign-up.

How do I generate a printable PDF guest list for the event?

Create a Cloud Function that queries event_guests for the event, formats the data as an HTML table, converts it to PDF using a library like pdf or puppeteer, uploads it to Firebase Storage, and returns the download URL. In FlutterFlow, add a Download Guest List button that calls this Cloud Function and opens the returned URL in a browser for download.

What is the maximum number of guests this architecture supports?

The separate event_guests collection approach scales to millions of guests per event. Firestore can handle this volume. The practical limit is your Cloud Function memory when sending bulk email invitations — process invitations in batches of 500 to avoid timeout issues. Use Cloud Tasks for very large invitation lists (10,000+) to queue emails as background tasks.

How do I handle plus-ones and group RSVPs?

Add a plus_one_count field to the event_guests document when the guest confirms. For group RSVPs, add a party_size field. Update the event's confirmed_count by the party_size amount rather than 1. Add a party_members array field to store additional guest names if you need them individually tracked for the check-in scanner.

Can I send reminder notifications to guests who have not RSVPed?

Yes. Create a scheduled Cloud Function that runs daily, queries event_guests where status equals 'invited' and invited_at is more than 48 hours ago and the event date is in the future. Send a reminder email to each. Add a reminder_sent boolean field to avoid sending multiple reminders and update it after the first reminder is sent.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.