# How to Create a Dynamic Questionnaire System in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 20-25 min
- Compatibility: FlutterFlow Free+
- Last updated: March 2026

## TL;DR

Build a questionnaire system that reads question definitions from Firestore, including input type, validation rules, and options. Render each question dynamically using Generate Dynamic Children with a Conditional Builder switching between TextField, DateTimePicker, RadioButton, and CheckboxGroup based on the inputType field. Collect all answers in a Page State Map, validate required fields and regex patterns on submit, and save responses to a questionnaire_responses collection.

## Building a Dynamic Questionnaire System in FlutterFlow

Hardcoded questionnaires require an app update for every change. A dynamic system reads questions from Firestore at runtime, rendering the correct input widget for each question type. Admins can add, remove, or modify questions without touching the app. This tutorial covers schema design, dynamic rendering, validation, and response storage.

## Before you start

- A FlutterFlow project with Firestore configured
- A questionnaires collection with a questions subcollection in Firestore
- Understanding of Conditional Builder and Page State in FlutterFlow
- Basic knowledge of regular expressions for input validation

## Step-by-step guide

### 1. Define the questionnaire schema in Firestore

Create a questionnaires collection with fields: title (String), description (String), isActive (Boolean). Under each questionnaire, create a questions subcollection. Each question document contains: questionText (String), inputType (String: text, number, email, phone, date, singleSelect, multiSelect), options (String Array for select types), isRequired (Boolean), placeholder (String), validationRegex (String, optional), order (int). Add five to six test questions with varied input types and at least two with validation regex.

**Expected result:** Firestore contains a questionnaire with typed question documents including validation patterns.

### 2. Load questions and render dynamically with Conditional Builder

Create a QuestionnairePage with a Route Parameter questionnaireId. Add a Backend Query on the questions subcollection ordered by the order field. Add a Column with Generate Dynamic Children bound to the query results. Inside each dynamic child, use a Conditional Builder checking inputType: text and number map to TextField (number gets keyboardType number), email maps to TextField with email keyboard, phone to TextField with phone keyboard, date to DateTimePicker, singleSelect to RadioButton group populated from the options array, multiSelect to a CheckboxGroup with the options. Place the questionText as a label Text above each input.

**Expected result:** The questionnaire renders the correct input widget for each question based on its Firestore inputType.

### 3. Collect answers in a Page State Map on input change

Add a Page State variable answers (JSON type, default empty map) and a variable submitAttempted (Boolean, default false). On each input widget's On Changed callback, update the answers Map: set the key as the question document ID and the value as the entered data. For singleSelect, store the selected option string. For multiSelect, store the selected options as a list. For date, store the selected DateTime. This accumulates all responses in one structured Map for validation and submission.

**Expected result:** Every answer change updates the answers Map with the question ID as key and the user input as value.

### 4. Validate required fields and regex patterns before submission

On the Submit button tap, first set submitAttempted to true. Loop through the question documents. For each question where isRequired is true, check that answers[questionId] exists and is not empty. For questions with a validationRegex, check that the answer matches the pattern using a Custom Function that calls RegExp(regex).hasMatch(value). If any validation fails, show a SnackBar listing the first failing question. Display inline red error text below each invalid field using Conditional Visibility: show when submitAttempted is true AND the field fails validation.

```
// Custom Function: validateRegex
bool validateRegex(String value, String regex) {
  if (regex.isEmpty) return true;
  return RegExp(regex).hasMatch(value);
}
```

**Expected result:** Invalid or missing answers show red inline errors and the form does not submit until all validations pass.

### 5. Save the response and show completion feedback

After all validations pass, create a document in questionnaire_responses with fields: questionnaireId, userId (current user or anonymous identifier), answers (the complete Map), submittedAt (server timestamp). Show a success SnackBar or navigate to a thank-you page. Add a LinearPercentIndicator at the top of the page bound to the number of answered questions divided by total questions to show progress as the user fills in responses.

**Expected result:** Valid responses are saved to Firestore with a progress bar showing completion rate during filling.

## Complete code example

File: `FlutterFlow Dynamic Questionnaire Setup`

```text
FIRESTORE DATA MODEL:
  questionnaires/{questionnaireId}
    title: String
    description: String
    isActive: Boolean
    └── questions/{questionId}
          questionText: String
          inputType: "text" | "number" | "email" | "phone" | "date" | "singleSelect" | "multiSelect"
          options: ["Option A", "Option B"] (select types only)
          isRequired: Boolean
          placeholder: String
          validationRegex: String (optional, e.g. "^[\\w.-]+@[\\w.-]+\\.\\w+$")
          order: int

  questionnaire_responses/{responseId}
    questionnaireId: String
    userId: String
    answers: Map { questionId: answer }
    submittedAt: Timestamp

PAGE STATE:
  answers: JSON Map = {}
  submitAttempted: Boolean = false

WIDGET TREE:
  Column (padding: 16)
    ├── Text (questionnaire.title, Headline Small)
    ├── Text (questionnaire.description, Body Medium)
    ├── LinearPercentIndicator (answered count / total questions)
    ├── SizedBox (16)
    ├── Column (Generate Dynamic Children → questions ordered by order)
    │     └── Per question:
    │           Column
    │             ├── Text (questionText + " *" if required)
    │             ├── SizedBox (8)
    │             ├── Conditional Builder (inputType)
    │             │     text → TextField (placeholder)
    │             │     number → TextField (keyboardType: number)
    │             │     email → TextField (keyboardType: email)
    │             │     phone → TextField (keyboardType: phone)
    │             │     date → DateTimePicker
    │             │     singleSelect → RadioButton (options)
    │             │     multiSelect → CheckboxGroup (options)
    │             ├── Text (error msg, red, visible if invalid & submitAttempted)
    │             └── SizedBox (16)
    └── Button "Submit" (Primary, full width)
          On Tap:
            1. Set submitAttempted = true
            2. Validate required + regex
            3. If valid → Create questionnaire_responses doc
            4. Show SnackBar success or navigate to thank-you
```

## Common mistakes

- **Not validating email and phone fields with regex before submission** — Without regex validation, users submit malformed email addresses or phone numbers. The collected data becomes unreliable for follow-up contact. Fix: Store a validationRegex on each question document and check it with RegExp.hasMatch before allowing submission.
- **Using Conditional Visibility toggles instead of Conditional Builder for input types** — Conditional Visibility keeps all input widgets in the tree but hidden. With six input types per question across 20 questions, that creates 120 hidden widgets eating memory. Fix: Use Conditional Builder which only builds the one matching widget, keeping the tree lean.
- **Hardcoding questions in the widget tree instead of loading from Firestore** — Every question change requires editing the FlutterFlow project and republishing. With multiple questionnaires this becomes unmanageable. Fix: Store all questions in Firestore and render dynamically so admins can modify questionnaires without app changes.

## Best practices

- Use order values with gaps (10, 20, 30) so new questions can be inserted between existing ones
- Store validation regex on the question document so rules travel with the question
- Show a progress indicator based on answered questions to encourage completion
- Validate both required fields and regex patterns before allowing submission
- Use Conditional Builder instead of Conditional Visibility for input type switching
- Save anonymous user identifier when no authentication is required
- Add placeholder text to guide users on expected input format for each field

## Frequently asked questions

### How do I add conditional question branching?

Add dependsOn fields to each question document (dependsOnQuestionId, dependsOnValue). In the rendering logic, check if answers[dependsOnQuestionId] matches the required value before showing the question with Conditional Visibility.

### Can I paginate questions across multiple screens?

Yes. Group questions by a pageNumber field and use a PageView to navigate between pages. Show only questions matching the current page number in each view.

### How do I allow users to save partial progress and resume later?

Save the current answers Map to a draft_responses collection on a Save button tap or on page dispose. On return, load the draft and pre-fill the answers Map and input widgets.

### Can I export questionnaire responses to a spreadsheet?

Create a Cloud Function that queries questionnaire_responses, maps each answer Map to column headers from the questions, and generates a CSV file for download.

### What regex should I use for phone number validation?

A simple pattern like ^\+?[0-9]{7,15}$ accepts international phone numbers with an optional plus prefix and 7 to 15 digits. Adjust based on your target countries.

### Can RapidDev help build a questionnaire platform?

Yes. RapidDev can build complete questionnaire systems with branching logic, multi-page navigation, response analytics dashboards, and CSV export features.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-dynamic-questionnaire-system-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-dynamic-questionnaire-system-in-flutterflow
