Build a full quiz platform with timed questions using PageView navigation, RadioButton answer selection, and a countdown timer. Scores are calculated and validated server-side via a Cloud Function that compares submitted answers against correct answers stored in Firestore. A leaderboard page queries quiz_results ordered by score descending and time taken ascending, displaying ranked entries with gold, silver, and bronze styling for the top three players.
Building a Competitive Quiz Platform in FlutterFlow
This tutorial walks you through creating a quiz platform where users answer timed multiple-choice questions and compete on a leaderboard. Unlike a simple quiz widget, this is a full platform with multiple quizzes, server-validated scoring to prevent cheating, and a ranked leaderboard that breaks ties by time taken. It is ideal for e-learning apps, trivia games, and employee training tools.
Prerequisites
- A FlutterFlow project with Firestore configured
- Basic understanding of PageView and Component State in FlutterFlow
- A Firebase project with Cloud Functions enabled (Blaze plan)
- Familiarity with creating Firestore collections and documents
Step-by-step guide
Set up the Firestore data model for quizzes, questions, and results
Set up the Firestore data model for quizzes, questions, and results
Create a quizzes collection with fields: title (String), description (String), category (String), questionCount (int), timeLimit (int, seconds). Under each quiz document, create a questions subcollection with fields: questionText (String), options (String array of 4 items), correctIndex (int, 0-3), order (int). Create a top-level quiz_results collection with fields: userId (String), quizId (String), score (int), totalQuestions (int), timeTaken (int, seconds), completedAt (Timestamp). Add 5-10 test questions for at least one quiz.
Expected result: Firestore has quizzes with a questions subcollection and a quiz_results collection ready for score storage.
Build the quiz page with timed PageView navigation
Build the quiz page with timed PageView navigation
Create a QuizPage with Route Parameter quizId (String). Add a Backend Query for the questions subcollection ordered by order ascending. Add Component State: currentIndex (int, default 0), selectedAnswerIndex (int, default -1), selectedAnswers (int List, empty), startTime (Timestamp, set on page load). Place a Row at the top with a LinearPercentIndicator for question progress and a Text widget showing the countdown timer. Use a Periodic Action (every 1 second) that decrements the remaining time from the quiz timeLimit. Below, add a PageView with NeverScrollableScrollPhysics containing one question per page.
Expected result: A quiz page loads questions from Firestore with a visible countdown timer and progress bar.
Display questions with RadioButton answer selection
Display questions with RadioButton answer selection
Inside each PageView page, add a Column with the questionText in Headline Small style, a SizedBox for spacing, and a RadioButton group bound to the options array. When the user taps an option, update Component State selectedAnswerIndex. Below the options, add a Next button that is disabled when selectedAnswerIndex equals -1. On Next tap: append selectedAnswerIndex to the selectedAnswers list, reset selectedAnswerIndex to -1, increment currentIndex, and call Animate to Next Page. On the last question, change button text to Submit.
Expected result: Each question displays with four selectable options, and the Next button advances through questions one at a time.
Calculate and validate the score via a Cloud Function
Calculate and validate the score via a Cloud Function
Create a Cloud Function called calculateQuizScore that accepts userId, quizId, and selectedAnswers array. The function reads the questions subcollection server-side, compares each submitted answer to the correctIndex, tallies the score, calculates timeTaken from startTime to now, and writes a quiz_results document with userId, quizId, score, totalQuestions, timeTaken, and completedAt. On the Submit button tap in FlutterFlow, call this Cloud Function via an API Call action, passing the Component State data. This prevents client-side score manipulation.
1// Cloud Function: calculateQuizScore2const functions = require('firebase-functions');3const admin = require('firebase-admin');4admin.initializeApp();56exports.calculateQuizScore = functions.https.onCall(async (data, context) => {7 const { quizId, selectedAnswers } = data;8 const userId = context.auth.uid;9 const questionsSnap = await admin.firestore()10 .collection('quizzes').doc(quizId)11 .collection('questions').orderBy('order').get();1213 let score = 0;14 questionsSnap.docs.forEach((doc, i) => {15 if (doc.data().correctIndex === selectedAnswers[i]) score++;16 });1718 const result = {19 userId,20 quizId,21 score,22 totalQuestions: questionsSnap.size,23 timeTaken: Math.floor((Date.now() - data.startTime) / 1000),24 completedAt: admin.firestore.FieldValue.serverTimestamp(),25 };26 await admin.firestore().collection('quiz_results').add(result);27 return result;28});Expected result: Score is calculated server-side and saved to quiz_results, preventing any client-side cheating.
Display the results page with score breakdown
Display the results page with score breakdown
After the Cloud Function returns, navigate to a ResultsPage passing score, totalQuestions, and timeTaken as Route Parameters. Display a CircularPercentIndicator showing the percentage score, a Text with the fraction (e.g., 8 out of 10), and the time taken. Add a Lottie animation for celebration if score is above 70 percent, or encouragement if below. Include a Retake Quiz button that navigates back to QuizPage and a View Leaderboard button.
Expected result: A results screen shows the score with visual feedback, time taken, and navigation options.
Build the leaderboard page with ranked entries
Build the leaderboard page with ranked entries
Create a LeaderboardPage with Route Parameter quizId. Add a Backend Query on quiz_results filtered by quizId, ordered by score descending then timeTaken ascending (Firestore composite index required). Display the top 3 entries in a special Row with gold, silver, and bronze Container styling using CircleImage avatars and score Text. Below, show remaining entries in a ListView with rank numbers, display names, scores, and time taken. Highlight the current user's row with a distinct background color. Add ChoiceChips for filtering by Today, This Week, or All Time using completedAt date ranges.
Expected result: A ranked leaderboard displays quiz results with the top three highlighted and the current user's position visible.
Complete working example
1FIRESTORE DATA MODEL:2 quizzes/{quizId}3 title: String4 description: String5 category: String6 questionCount: int7 timeLimit: int (seconds)8 └── questions/{questionId}9 questionText: String10 options: ["A", "B", "C", "D"]11 correctIndex: int (0-3)12 order: int1314 quiz_results/{resultId}15 userId: String16 quizId: String17 score: int18 totalQuestions: int19 timeTaken: int (seconds)20 completedAt: Timestamp2122PAGE: QuizPage23 Route Parameter: quizId (String)24 Backend Query: questions subcollection, order by order asc25 Periodic Action: every 1s, decrement timer2627COMPONENT STATE:28 currentIndex: int = 029 selectedAnswerIndex: int = -130 selectedAnswers: List<int> = []31 remainingSeconds: int = quiz.timeLimit32 startTime: int = DateTime.now().millisecondsSinceEpoch3334WIDGET TREE (QuizPage):35 Column36 ├── Row37 │ ├── LinearPercentIndicator (currentIndex / totalQuestions)38 │ └── Text (remainingSeconds formatted as mm:ss)39 ├── PageView (NeverScrollableScrollPhysics)40 │ └── Per question:41 │ Column42 │ ├── Text (questionText, Headline Small)43 │ └── RadioButtonGroup (options, selectedAnswerIndex)44 └── Button ("Next" / "Submit")45 On Tap: append answer → next page or call Cloud Function4647PAGE: ResultsPage48 Route Params: score, totalQuestions, timeTaken49 Column50 ├── CircularPercentIndicator (score / totalQuestions)51 ├── Text ("score out of totalQuestions")52 ├── Text (timeTaken formatted)53 ├── Lottie (celebration or try-again)54 ├── Button ("Retake Quiz")55 └── Button ("View Leaderboard")5657PAGE: LeaderboardPage58 Route Parameter: quizId59 Backend Query: quiz_results where quizId, orderBy score desc + timeTaken asc60 Column61 ├── Row (Top 3: gold / silver / bronze cards)62 ├── ChoiceChips (Today / Week / All Time)63 └── ListView (remaining entries with rank + avatar + score + time)Common mistakes when creating an Interactive Quiz Platform with Leaderboard in FlutterFlow
Why it's a problem: Calculating the score on the client and trusting the result
How to avoid: Send only the selectedAnswers array to a Cloud Function. The function reads correct answers server-side and calculates the score itself.
Why it's a problem: Using App State for quiz answers instead of Component State
How to avoid: Use Component State or Page State for in-progress quiz data. These reset when the page is left.
Why it's a problem: Not creating the Firestore composite index for leaderboard queries
How to avoid: Create a composite index on quiz_results: quizId (ascending), score (descending), timeTaken (ascending) in the Firebase Console.
Why it's a problem: Allowing users to swipe between PageView questions freely
How to avoid: Set PageView physics to NeverScrollableScrollPhysics so navigation only occurs via the Next button.
Best practices
- Validate scores server-side in a Cloud Function to prevent cheating
- Use NeverScrollableScrollPhysics on the PageView to enforce sequential question flow
- Show a countdown timer and auto-submit when time expires
- Store quiz results with timeTaken for meaningful leaderboard tiebreaking
- Display the current user's rank even if they are not in the top entries
- Disable the Next button until an answer is selected to prevent blank submissions
- Create Firestore composite indexes proactively for leaderboard sort queries
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a quiz platform in FlutterFlow with timed multiple-choice questions using PageView, server-side score validation via Cloud Functions, and a ranked leaderboard that breaks ties by time taken. Give me the Firestore data model, widget tree, action flows, and Cloud Function code.
Create a quiz page with a timer at the top right, a progress bar below it, a large question text in the center, four radio button answer options, and a Next button at the bottom. Add a leaderboard page with a top-3 podium section and a ranked list below.
Frequently asked questions
Can I randomize the question order for each quiz attempt?
Firestore does not support random ordering in queries. Fetch all questions, shuffle the list in a Custom Function before binding to the PageView, and use the shuffled list for display and answer tracking.
How do I prevent users from retaking a quiz?
On page load, query quiz_results for the current userId and quizId. If a document exists, redirect to the results page showing their previous score instead of starting the quiz.
Can I add different difficulty levels to quizzes?
Yes. Add a difficulty field to each quiz document. On the quiz catalog page, use ChoiceChips to filter quizzes by difficulty. You can also weight scoring by difficulty in the Cloud Function.
How do I show which answers were correct after submission?
On the results page, display each question with the user's selected answer and the correct answer. Use Conditional Styling to color correct answers green and incorrect ones red.
What happens if the timer runs out mid-quiz?
Set up the Periodic Action to check when remainingSeconds reaches zero. When it does, auto-submit whatever answers have been collected so far by calling the Cloud Function with the partial selectedAnswers array.
Can RapidDev help build a full quiz and e-learning platform?
Yes. RapidDev can build complete quiz platforms with adaptive difficulty, certificate generation, analytics dashboards, and multi-format question types beyond multiple choice.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation