Build an RPG character builder in FlutterFlow using a Firestore characters collection with a stats Map field. A PageView lets users swipe through class choices. Six Sliders for strength, agility, intelligence, stamina, charisma, and luck are constrained by a shared point pool tracked in Page State. Derived stats calculate in real time via Custom Functions. A Cloud Function validates the final build server-side before saving.
Full RPG Character Creation from Class to Skills
This tutorial builds a complete RPG character creator inside FlutterFlow. Users begin by swiping through a PageView of character classes (Warrior, Mage, Rogue, Ranger) with unique base stats and visual art. On the stats screen, six Sliders control individual stats while a shared point pool prevents cheating — allocating more to Strength means less for other stats. Custom Functions recalculate derived stats (HP, attack, defense, magic) in real time as sliders move. A GridView skill tree shows available abilities based on the chosen class. An avatar gallery lets users pick a profile image. On save, a Cloud Function validates the total allocated points match the allowed pool before writing the character to Firestore.
Prerequisites
- FlutterFlow account with Firebase connected and Firestore enabled
- Firebase Authentication enabled
- Basic familiarity with FlutterFlow Custom Functions and Components
- Firebase Cloud Functions enabled (for server-side validation)
Step-by-step guide
Design the Firestore Characters Collection Schema
Design the Firestore Characters Collection Schema
In Firestore, create a characters collection with fields: userId (String), name (String), className (String), level (Integer, default 1), avatarUrl (String), stats (Map — contains keys: strength, agility, intelligence, stamina, charisma, luck, each with Integer values), derivedStats (Map — contains: hp, maxHp, attack, defense, magic, speed), skills (Array of Strings — skill IDs), totalPointsSpent (Integer), createdAt (Timestamp). In FlutterFlow's Firestore panel, import the schema. The stats Map approach stores all six stats in one field rather than six separate fields — this makes batch updates efficient and lets you iterate over all stats in Custom Functions.
Expected result: The characters collection schema is created in Firestore. FlutterFlow shows the CharactersRecord Document Type with stats as a Map field.
Build the Class Selection Screen with PageView
Build the Class Selection Screen with PageView
Create a ClassSelection page. Add four Page State variables: selectedClass (String, default 'warrior'), classBaseStats (Map — stores the base stat bonuses for the selected class), currentPage (Integer, default 0). Add a PageView widget. Inside it, create four class cards — one per page. Each card has a class illustration Image, a class name Text, a description Text, and a list of base stat bonuses (e.g., Warrior: +3 Strength, +2 Stamina). When the PageView's page changes, update selectedClass and load the corresponding base stats into classBaseStats. Add Next and Previous buttons below the PageView that call ScrollTo actions targeting the PageView. Add a Select This Class button that navigates to the StatsAllocation page, passing selectedClass and classBaseStats as parameters.
Expected result: Users can swipe through class cards. Selecting a class navigates to stats allocation with the class data passed as a page parameter.
Build the Stat Sliders with a Shared Point Pool
Build the Stat Sliders with a Shared Point Pool
Create a StatsAllocation page with parameters: selectedClass (String) and classBaseStats (Map). Add Page State variables: strength (Integer, default 1), agility (Integer, default 1), intelligence (Integer, default 1), stamina (Integer, default 1), charisma (Integer, default 1), luck (Integer, default 1), pointsRemaining (Integer — set default to totalPointPool, e.g., 20). Add six Slider widgets, one per stat. Each Slider has a min of 1 and a max that is dynamically calculated as the stat's current value plus pointsRemaining — this prevents the slider from exceeding the available pool. Bind each Slider to its Page State variable. On each Slider's On Change action: calculate the delta (newValue minus currentValue), update the stat variable, and subtract the delta from pointsRemaining. Display pointsRemaining prominently so users can see how many points they have left.
1// Custom Function: calculateDerivedStats2// Arguments: strength (int), agility (int), intelligence (int),3// stamina (int), charisma (int), luck (int), className (String)4// Return type: String (JSON)5import 'dart:convert';67String calculateDerivedStats(8 int strength, int agility, int intelligence,9 int stamina, int charisma, int luck, String className,10) {11 final hp = 50 + (stamina * 10) + (strength * 5);12 final attack = (strength * 3) + (agility * 2);13 final defense = (stamina * 2) + (strength * 1);14 final magic = (intelligence * 4) + (luck * 2);15 final speed = (agility * 3) + (luck * 1);16 return json.encode({17 'hp': hp, 'maxHp': hp,18 'attack': attack, 'defense': defense,19 'magic': magic, 'speed': speed,20 });21}Expected result: Six sliders are displayed. Increasing one stat reduces available points. The pointsRemaining counter updates in real time. No slider can exceed current value plus remaining points.
Build the Skill Tree GridView
Build the Skill Tree GridView
Create a SkillTree page. Add a Backend Query fetching from a skills Firestore collection, filtered by className equals the selected class parameter. The skills collection has fields: id (String), name (String), description (String), iconUrl (String), requiredLevel (Integer), prerequisiteSkillId (String — for unlocking chains), className (String). Add a GridView showing 3 columns of skill cards. Each card shows the skill icon, name, and a lock/unlock indicator based on whether the user's level meets requiredLevel. On tap of an unlocked skill, add it to a Page State variable selectedSkills (List of Strings). Show the selected state with a highlight overlay using Conditional Visibility. Cap the maximum selectable skills using a Conditional on the tap action that checks selectedSkills length before adding.
Expected result: A skill tree grid shows available class skills. Eligible skills are selectable. Already-selected skills show a highlighted state.
Validate and Save the Character via Cloud Function
Validate and Save the Character via Cloud Function
On the final confirmation screen, show a summary of the character: class, stats, derived stats, and selected skills. The Save Character button calls a Firebase Cloud Function named validateAndSaveCharacter instead of writing directly to Firestore. The Cloud Function receives the character data, recalculates the total points spent (sum of all stat values minus 6, since minimum is 1 for each stat), verifies it does not exceed the allowed pool (e.g., 20), checks that selectedSkills belong to the chosen class, and then writes the document to Firestore. In FlutterFlow, create an API call targeting this Cloud Function. On success, navigate to the character's profile screen. On failure, show a Snack Bar with the validation error.
Expected result: Saving a character with invalid stats is rejected by the Cloud Function with an error message. Valid characters are saved to Firestore and the user is navigated to their character profile.
Complete working example
1// Custom Function: calculateDerivedStats2// FlutterFlow Custom Functions panel3// Arguments:4// strength, agility, intelligence, stamina, charisma, luck: int5// className: String6// Return type: String (JSON-encoded derived stats map)78import 'dart:convert';910String calculateDerivedStats(11 int strength,12 int agility,13 int intelligence,14 int stamina,15 int charisma,16 int luck,17 String className,18) {19 // Base derived stats formula20 final hp = 50 + (stamina * 10) + (strength * 5);21 final attack = (strength * 3) + (agility * 2);22 final defense = (stamina * 2) + (strength * 1);23 final magic = (intelligence * 4) + (luck * 2);24 final speed = (agility * 3) + (luck * 1);25 final critChance = ((luck / 10) * 100).round(); // percent2627 // Class-specific bonuses28 int bonusHp = 0;29 int bonusAttack = 0;30 int bonusMagic = 0;31 switch (className.toLowerCase()) {32 case 'warrior':33 bonusHp = 30;34 bonusAttack = 5;35 break;36 case 'mage':37 bonusMagic = 15;38 break;39 case 'rogue':40 bonusAttack = 8;41 break;42 case 'ranger':43 bonusAttack = 4;44 bonusHp = 10;45 break;46 }4748 final finalHp = hp + bonusHp;49 final finalAttack = attack + bonusAttack;50 final finalMagic = magic + bonusMagic;5152 return json.encode({53 'hp': finalHp,54 'maxHp': finalHp,55 'attack': finalAttack,56 'defense': defense,57 'magic': finalMagic,58 'speed': speed,59 'critChance': critChance,60 });61}6263// Custom Function: getRemainingPoints64// Calculates remaining allocation points65// Arguments: strength, agility, intelligence, stamina, charisma, luck (int), totalPool (int)66// Return type: int67int getRemainingPoints(68 int strength, int agility, int intelligence,69 int stamina, int charisma, int luck, int totalPool,70) {71 final spent = (strength - 1) + (agility - 1) + (intelligence - 1)72 + (stamina - 1) + (charisma - 1) + (luck - 1);73 return totalPool - spent;74}Common mistakes when creating a Role-Playing Game Character Builder in FlutterFlow
Why it's a problem: Not enforcing the total stat point constraint on the server
How to avoid: Use a Cloud Function to validate the build before saving. Recalculate totalPointsSpent from the raw stat values server-side and reject any build where the sum exceeds the allowed pool.
Why it's a problem: Using six separate Firestore fields for stats (strength, agility, intelligence...) instead of a Map
How to avoid: Store all stats in a single stats Map field. This allows updating all stats in one Firestore write and iterating over them generically in Custom Functions.
Why it's a problem: Setting all Slider maximums to the same hardcoded value (e.g., 10) regardless of remaining points
How to avoid: Calculate each Slider's maximum dynamically as currentStatValue plus pointsRemaining. This creates a hard ceiling that visually prevents over-allocation.
Best practices
- Store class definitions in Firestore rather than hardcoding them so you can add new classes or balance existing ones without republishing.
- Validate all stat totals server-side in a Cloud Function — never trust the client to enforce game rules.
- Use a stats Map field rather than individual stat fields to keep Firestore writes atomic and Custom Functions clean.
- Show real-time derived stat previews (HP, attack, defense) as sliders are moved so users understand the impact of each allocation decision.
- Add a Reset Stats button that returns all sliders to their minimum values and restores the full point pool.
- Cap the Slider maximum dynamically at currentValue + pointsRemaining to make the constraint obvious in the UI.
- Store the character as a draft in Firestore (isDraft: true) as the user progresses through the builder, and mark it complete when the Cloud Function validates and saves — this preserves partial progress.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building an RPG character builder in FlutterFlow. I have six stat sliders (strength, agility, intelligence, stamina, charisma, luck) that share a point pool of 20. The user starts with 1 in each stat. Write a system for managing this in FlutterFlow Page State — how should I track remaining points and dynamically set each Slider's maximum so users cannot exceed the pool total?
Create a Custom Function in FlutterFlow called calculateDerivedStats that takes six integer arguments (strength, agility, intelligence, stamina, charisma, luck) and a className String. Return a JSON-encoded String containing hp, attack, defense, magic, and speed values calculated from the stats. Apply a +30 HP bonus for the warrior class.
Frequently asked questions
How do I save in-progress character builds so users can come back later?
Write a partial character document to Firestore with isDraft: true as soon as the user selects a class. Update the document as the user progresses through each step. On the characters list screen, show draft characters with a 'Continue Building' button. Mark isDraft as false only after the Cloud Function validates the final build.
Can I let users have multiple characters?
Yes — the characters collection supports multiple documents per user since each document has a userId field, not a constraint on uniqueness. Show a character list screen that queries characters filtered by userId. Add a Create New Character button that starts the class selection flow.
How do I handle character leveling after the initial creation?
Create a levelUp Cloud Function that adds a configurable number of allocation points to the character's available pool. Build a Level Up screen in FlutterFlow that lets users allocate these new points using the same Slider system, then calls the Cloud Function to validate and apply the upgrade.
Why use a Cloud Function for validation instead of Firestore Security Rules?
Firestore Security Rules can validate individual field values but cannot easily compute cross-field constraints like 'the sum of all stats must not exceed 20'. Cloud Functions can run any logic, making them the right tool for game balance validation. Security Rules handle authentication and basic field-level access; Cloud Functions handle business logic.
How do I show skill prerequisite trees visually?
Build a Custom Widget using Flutter's CustomPainter to draw lines between skill nodes in the GridView. Each skill card stores a prerequisiteSkillId. Draw a line from the prerequisite's grid position to the current skill's position. Alternatively, use a third-party Flutter package like graphview or flutter_flow_chart for tree visualization.
Can I import character builds from other apps or export to JSON?
Yes. Create a Custom Action that serializes the character's stats, class, and skills to a JSON string and uses share_plus to share it. For import, add a TextField that accepts a pasted JSON string and a Custom Action that parses it and prefills the builder's Page State variables.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation