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

How to Create a Virtual Event Platform in FlutterFlow

Build a virtual event platform using Firestore collections for events, attendees, and Q&A questions. Embed live video via a WebView Custom Widget pointing to YouTube Live or similar streaming URLs. Add real-time chat with a reversed ListView of Firestore messages, and a Q&A panel where attendees submit and upvote questions for the host to address during the broadcast.

What you'll learn

  • How to design Firestore collections for events, attendees, and Q&A
  • How to embed a live video stream using a WebView Custom Widget
  • How to build real-time chat and upvote-based Q&A panels
  • How to handle event registration and post-event recording delivery
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read25-30 minFlutterFlow Pro+ (Custom Code required for WebView)March 2026RapidDev Engineering Team
TL;DR

Build a virtual event platform using Firestore collections for events, attendees, and Q&A questions. Embed live video via a WebView Custom Widget pointing to YouTube Live or similar streaming URLs. Add real-time chat with a reversed ListView of Firestore messages, and a Q&A panel where attendees submit and upvote questions for the host to address during the broadcast.

Building a Virtual Event Platform in FlutterFlow

Virtual events are essential for webinars, conferences, and workshops. This tutorial walks you through building a one-to-many broadcast platform with audience interaction features including live chat, Q&A with upvoting, attendee registration, and post-event recording access — all wired into FlutterFlow's visual builder with Firestore as the backend.

Prerequisites

  • A FlutterFlow project on the Pro plan or higher
  • Firebase project with Firestore and Cloud Functions enabled
  • A streaming service account (YouTube Live, Vimeo Live, or similar) for the video embed URL
  • Basic familiarity with Firestore collections and Backend Queries in FlutterFlow

Step-by-step guide

1

Create the Firestore collections for events and attendees

In the Firebase Console, create an `events` collection with fields: title (String), description (String), startTime (Timestamp), endTime (Timestamp), type (String — webinar, conference, or workshop), streamUrl (String), maxAttendees (int), hostId (String), recordingUrl (String), and isLive (bool). Then create a subcollection `events/{eventId}/attendees` with fields: userId (String), registeredAt (Timestamp), and hasJoined (bool). Back in FlutterFlow, go to the Firestore tab and import both schemas so they appear in your Backend Query options.

Expected result: Both collections appear in FlutterFlow's Firestore schema panel and are available for Backend Queries.

2

Build the event listing page with registration

Create an Events page with a Backend Query on the `events` collection ordered by startTime ascending. Drag a ListView and bind it to the query results. Inside each list item, add a Container with the event title (Text), date and time formatted with a Custom Function, event type badge (Container with colored background), and attendee count. Add a Register button that creates a new document in the `attendees` subcollection with the current user's ID, sets registeredAt to the current timestamp, and hasJoined to false. Use Conditional Visibility to show 'Registered' text instead of the button if the user already has an attendee document.

Expected result: Users see upcoming events in a list and can register with a single tap. The button switches to a Registered label after signing up.

3

Embed the live video stream with a WebView Custom Widget

Create a Custom Widget named LiveStreamPlayer with a Component Parameter streamUrl (String). In the build method, return a SizedBox wrapping a WebView widget that loads the streamUrl (e.g., a YouTube Live embed URL like https://www.youtube.com/embed/VIDEO_ID?autoplay=1). Set the widget height to 250 and width to double.infinity. On the Event Detail page, add this Custom Widget and bind the streamUrl parameter to the event document's streamUrl field. Wrap it in Conditional Visibility that checks event.isLive == true so the player only appears during live broadcasts.

live_stream_player.dart
1// Custom Widget: LiveStreamPlayer
2import 'package:flutter/material.dart';
3import 'package:webview_flutter/webview_flutter.dart';
4
5class LiveStreamPlayer extends StatefulWidget {
6 final double width;
7 final double height;
8 final String streamUrl;
9
10 const LiveStreamPlayer({
11 Key? key,
12 required this.width,
13 required this.height,
14 required this.streamUrl,
15 }) : super(key: key);
16
17 @override
18 State<LiveStreamPlayer> createState() => _LiveStreamPlayerState();
19}
20
21class _LiveStreamPlayerState extends State<LiveStreamPlayer> {
22 late final WebViewController _controller;
23
24 @override
25 void initState() {
26 super.initState();
27 _controller = WebViewController()
28 ..setJavaScriptMode(JavaScriptMode.unrestricted)
29 ..loadRequest(Uri.parse(widget.streamUrl));
30 }
31
32 @override
33 Widget build(BuildContext context) {
34 return SizedBox(
35 width: widget.width,
36 height: widget.height,
37 child: WebViewWidget(controller: _controller),
38 );
39 }
40}

Expected result: The live video stream renders inside the event detail page when the event is marked as live.

4

Add real-time chat alongside the stream

Create a subcollection `events/{eventId}/messages` with fields: senderId (String), displayName (String), text (String), and timestamp (Timestamp). On the Event Detail page below the video player, add a Column with a ListView bound to a Backend Query on the messages subcollection ordered by timestamp descending. Set Single Time Query to OFF for real-time updates. Inside each list item, show the sender's displayName in bold followed by the message text. At the bottom, add a Row with a TextField for message input and a Send IconButton. The Send action creates a new document in the messages subcollection with the current user info and the text value, then clears the TextField.

Expected result: Attendees see live chat messages appearing in real time and can send their own messages during the event.

5

Build the Q&A panel with upvoting

Create a subcollection `events/{eventId}/questions` with fields: userId (String), displayName (String), text (String), upvotes (int), isAnswered (bool), and timestamp (Timestamp). Add a TabBar on the event detail page with two tabs: Chat and Q&A. In the Q&A tab, add a ListView bound to the questions subcollection ordered by upvotes descending. Each item shows the question text, author name, upvote count, and an upvote IconButton. The upvote action uses FieldValue.increment(1) on the upvotes field. To prevent double-voting, create a subcollection `questions/{questionId}/voters` and check for the current user's document before allowing the upvote. The host sees an 'Mark Answered' button (Conditional Visibility: currentUser.uid == event.hostId) that sets isAnswered to true.

Expected result: Attendees submit questions and upvote others. The most popular questions rise to the top for the host to address.

6

Handle post-event recording and feedback

After the event ends, the host updates the event document to set isLive to false and add the recordingUrl field pointing to the saved stream recording. On the Event Detail page, add a Conditional Visibility block that shows when isLive is false AND recordingUrl is not empty — display a Video Player widget or WebView with the recording URL. Below it, add a feedback section: a StarRating widget (1-5) and a TextField for comments. On submit, create a document in `events/{eventId}/feedback` with the user's rating, comment, and timestamp. Show a summary of average rating using a Custom Function that queries the feedback subcollection.

Expected result: After the event, attendees can watch the recording and leave feedback. The average rating displays below the recording.

Complete working example

LiveStreamPlayer Custom Widget
1// Custom Widget: LiveStreamPlayer
2// Pubspec: webview_flutter: ^4.4.0
3
4import 'package:flutter/material.dart';
5import 'package:webview_flutter/webview_flutter.dart';
6
7class LiveStreamPlayer extends StatefulWidget {
8 final double width;
9 final double height;
10 final String streamUrl;
11
12 const LiveStreamPlayer({
13 Key? key,
14 required this.width,
15 required this.height,
16 required this.streamUrl,
17 }) : super(key: key);
18
19 @override
20 State<LiveStreamPlayer> createState() => _LiveStreamPlayerState();
21}
22
23class _LiveStreamPlayerState extends State<LiveStreamPlayer> {
24 late final WebViewController _controller;
25
26 @override
27 void initState() {
28 super.initState();
29 _controller = WebViewController()
30 ..setJavaScriptMode(JavaScriptMode.unrestricted)
31 ..loadRequest(Uri.parse(widget.streamUrl));
32 }
33
34 @override
35 Widget build(BuildContext context) {
36 return SizedBox(
37 width: widget.width,
38 height: widget.height,
39 child: WebViewWidget(controller: _controller),
40 );
41 }
42}
43
44// Cloud Function: sendEventReminder
45// Triggered by Cloud Scheduler 30 min before event startTime
46// Queries attendees subcollection and sends FCM push
47// notification with event title and join link.
48
49// Firestore Structure:
50// events/{eventId}
51// - title, description, startTime, endTime
52// - type, streamUrl, maxAttendees, hostId
53// - recordingUrl, isLive
54// events/{eventId}/attendees/{attendeeId}
55// - userId, registeredAt, hasJoined
56// events/{eventId}/messages/{messageId}
57// - senderId, displayName, text, timestamp
58// events/{eventId}/questions/{questionId}
59// - userId, displayName, text, upvotes
60// - isAnswered, timestamp
61// events/{eventId}/feedback/{feedbackId}
62// - userId, rating, comment, timestamp

Common mistakes when creating a Virtual Event Platform in FlutterFlow

Why it's a problem: Using bidirectional video calls (Agora) for large webinars with hundreds of attendees

How to avoid: Use one-to-many streaming services like YouTube Live, Vimeo Live, or Agora Broadcast mode. Embed the stream URL in a WebView and use Firestore chat/Q&A for audience interaction.

Why it's a problem: Not setting Single Time Query to OFF on the chat and Q&A ListViews

How to avoid: Set Single Time Query to OFF on all Backend Queries for messages and questions so Firestore real-time listeners push updates to the UI automatically.

Why it's a problem: Allowing unlimited registrations beyond the event capacity

How to avoid: Before creating an attendee document, query the attendees subcollection count and compare it to maxAttendees. If full, show a 'Sold Out' message instead of the Register button.

Best practices

  • Use Conditional Visibility to show the live stream only when isLive is true, and the recording when available after the event ends
  • Order Q&A questions by upvotes descending so the host always sees the most popular questions first
  • Add a countdown timer on the event detail page using a Custom Function that calculates the difference between now and startTime
  • Store the host's userId on the event document so you can conditionally show admin controls like 'Go Live' and 'Mark Answered'
  • Limit chat message length to 500 characters to prevent spam and keep the conversation readable
  • Send FCM push notifications 30 minutes before the event starts via a scheduled Cloud Function to remind registered attendees
  • Use a TabBar to separate Chat and Q&A panels so the event detail page stays organized

Still stuck?

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

ChatGPT Prompt

I'm building a virtual event platform in FlutterFlow with Firestore. I need collections for events, attendees, live chat messages, and Q&A with upvoting. Show me the Firestore schema design and explain how to embed a YouTube Live stream in a WebView Custom Widget.

FlutterFlow Prompt

Create a virtual event page with a live stream video player at the top, a live chat panel below it with real-time Firestore messages, and a Q&A tab where attendees can submit and upvote questions. Add a Register button that creates an attendee document.

Frequently asked questions

Can I use Agora instead of YouTube Live for the video stream?

Yes. Agora supports broadcast mode for one-to-many streaming. Use the Agora Flutter SDK in a Custom Widget instead of a WebView. However, YouTube Live is simpler to integrate and has no per-minute streaming costs.

How do I prevent users from upvoting a Q&A question more than once?

Create a voters subcollection under each question document. Before incrementing upvotes, check if a document with the current user's ID already exists. If it does, show a 'You already voted' message instead of incrementing.

Can I limit the number of attendees for an event?

Yes. Store a maxAttendees field on the event document. Before creating an attendee document, query the attendees subcollection count. If it equals or exceeds maxAttendees, disable the Register button and show 'Event Full'.

How do I add the event to the attendee's calendar?

Use a Cloud Function that generates an .ics calendar file with the event title, start time, and end time. Attach it to the registration confirmation email or provide a download link on the event detail page.

Does the live chat work on both mobile and web?

Yes. The Firestore real-time listener powering the chat ListView works identically on iOS, Android, and FlutterFlow web builds.

Can RapidDev help build a production-ready virtual event platform?

Yes. RapidDev can implement multi-track conferences with breakout rooms, ticketing with Stripe, speaker management dashboards, and automated recording workflows.

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.