Implement user roles in FlutterFlow by adding a 'role' string field to each user's Firestore document, then use Conditional Visibility to show or hide UI elements based on the role. Critically, also add Firestore Security Rules that enforce the same role checks on the server side — UI-only gating can be bypassed. Common roles: 'admin', 'editor', 'viewer'.
Role-Based Access Control for FlutterFlow Apps
Most production apps need more than two states — logged in or logged out. A content platform needs editors who can publish and viewers who can only read. A SaaS product needs admins who can manage billing and regular users who cannot. A marketplace needs sellers who can create listings and buyers who cannot. Role-based access control (RBAC) solves this by assigning each user a role and enforcing permissions at both the UI level (showing the right interface) and the data level (preventing unauthorized reads and writes). In FlutterFlow, the role is stored as a field on the user's Firestore document, checked in Conditional Visibility rules for UI gating, and enforced in Firestore Security Rules for server-side data gating. This guide covers the full setup.
Prerequisites
- FlutterFlow project with Firebase Authentication and Firestore connected
- Users collection in Firestore (auto-created by FlutterFlow's auth setup)
- At least one page where you want to restrict access by role
- Access to the Firebase Console to edit Firestore Security Rules
Step-by-step guide
Add a role field to the Firestore users collection
Add a role field to the Firestore users collection
Open the Firebase Console → Firestore → your 'users' collection. Click on any user document and tap 'Add field'. Add a field named 'role' of type String with a default value of 'viewer' (or whichever role new users should receive). In FlutterFlow, go to the left sidebar → Firestore (database icon) → find your 'users' collection schema. Add the 'role' field as a String type there too so FlutterFlow knows about it when building data bindings. Update your app's user creation flow: in the Action Flow that runs after sign-up, after the Firebase 'Create User' step, add a 'Create Document' action in the users collection that sets the role field to 'viewer'. This ensures every new user has a role from the moment their account is created.
Expected result: Every user document in Firestore has a 'role' string field. New user registration automatically sets role to 'viewer'.
Fetch the user's role into authenticated user data
Fetch the user's role into authenticated user data
FlutterFlow's 'Authenticated User' context gives you access to Firebase Auth fields (email, UID, displayName) but not custom Firestore fields like 'role'. To access the role throughout the app, query the users Firestore document once on app start and store the role in App State. On your initial authenticated page (e.g., Home page), add an 'On Page Load' action that queries the users collection document where the document ID matches the current user's UID. After the query, add an 'Update App State' action that sets a 'userRole' App State variable (String type) to the Firestore document's role field. Now 'userRole' is available anywhere in the app for conditional checks.
Expected result: The App State 'userRole' variable contains the authenticated user's role string after the home page loads.
Apply Conditional Visibility to restrict UI elements
Apply Conditional Visibility to restrict UI elements
Navigate to a page that has elements only certain roles should see — for example, an 'Admin Dashboard' button or a 'Delete' action button. Select the widget you want to restrict. In the properties panel on the right, find the 'Conditional Visibility' section (sometimes labeled 'Visible When' or under Conditional settings). Add a condition: 'App State → userRole equals [string] admin'. The widget will only be visible when the current user has the admin role. Apply the same pattern to entire sections by wrapping them in a Column or Container and applying the condition to the container. Repeat for editor-only features using 'userRole equals editor'.
Expected result: Admin-only UI elements are hidden for viewer-role users and visible for admin-role users when you test in Test Mode with different user accounts.
Add Firestore Security Rules for server-side role enforcement
Add Firestore Security Rules for server-side role enforcement
Open the Firebase Console → Firestore → Rules tab. Add rules that check the role field on the user's document before allowing sensitive operations. The pattern is: function isRole(role) { return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == role; }. Use this function in your collection rules. For example, a 'content' collection where only admins can delete documents: 'allow delete: if isRole("admin");'. Role rules must be written in the Firebase Console — FlutterFlow's visual security rule editor does not yet support custom function definitions. Deploy the rules and test them using the Firebase Console's Rules Playground.
1rules_version = '2';2service cloud.firestore {3 match /databases/{database}/documents {45 // Helper: check caller's role from their user document6 function getRole() {7 return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;8 }9 function isAdmin() { return getRole() == 'admin'; }10 function isEditor() { return getRole() == 'editor' || isAdmin(); }11 function isViewer() { return request.auth != null; }1213 // Users collection: users can read/write their own doc; admins can read all14 match /users/{uid} {15 allow read: if request.auth.uid == uid || isAdmin();16 allow write: if request.auth.uid == uid;17 allow update: if isAdmin(); // admins can update any user (e.g. change role)18 }1920 // Content: viewers can read, editors can create/update, admins can delete21 match /content/{docId} {22 allow read: if isViewer();23 allow create, update: if isEditor();24 allow delete: if isAdmin();25 }26 }27}Expected result: Firestore Security Rules enforce role-based access on all sensitive collections. Test with the Rules Playground confirms admins can delete, editors can write, and viewers can only read.
Create an admin screen to assign and change roles
Create an admin screen to assign and change roles
Build an Admin Users page in FlutterFlow that lists all users and lets admins change their roles. Add a Repeating Group with a Backend Query on the users collection (admin-only access per Security Rules). Each list item shows the user's email and a Dropdown widget bound to the role field. Add an 'On Dropdown Change' action that calls 'Update Document' on the users collection, setting the role field to the new dropdown value. Protect the entire Admin page with a conditional on page load: if userRole is NOT 'admin', navigate away to the home page. This prevents non-admin users from accessing the admin URL directly.
Expected result: The admin screen lists all users with their current roles, and admins can change any user's role via the dropdown. Non-admin users are redirected to the home page.
Complete working example
1// Firestore Security Rules with Role-Based Access Control2// Deploy via: Firebase Console → Firestore → Rules tab3// Or: firebase deploy --only firestore:rules (requires code export)45rules_version = '2';6service cloud.firestore {7 match /databases/{database}/documents {89 // ── Role helpers ────────────────────────────────────────────────────────10 function getRole() {11 return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;12 }13 function isAdmin() {14 return request.auth != null && getRole() == 'admin';15 }16 function isEditor() {17 return request.auth != null && (getRole() == 'editor' || getRole() == 'admin');18 }19 function isAuthenticated() {20 return request.auth != null;21 }22 function isOwner(uid) {23 return request.auth.uid == uid;24 }2526 // ── Users collection ─────────────────────────────────────────────────────27 match /users/{uid} {28 // Users read their own doc; admins read all29 allow read: if isOwner(uid) || isAdmin();30 // Users update their own profile fields (not role)31 allow update: if isOwner(uid)32 && !('role' in request.resource.data.diff(resource.data).affectedKeys());33 // Only admins can change roles or create/delete user docs34 allow create, delete: if isAdmin();35 allow update: if isAdmin();36 }3738 // ── Content collection (articles, posts, etc.) ───────────────────────────39 match /content/{docId} {40 allow read: if isAuthenticated();41 allow create, update: if isEditor();42 allow delete: if isAdmin();43 }4445 // ── Audit log: admins write, no client reads ──────────────────────────────46 match /audit_log/{logId} {47 allow create: if isAdmin();48 allow read: if false; // Read via Admin SDK only49 }5051 // ── Analytics events: users write their own, no client reads ─────────────52 match /analytics_events/{eventId} {53 allow create: if isAuthenticated()54 && request.resource.data.uid == request.auth.uid;55 allow read: if false;56 }57 }58}Common mistakes
Why it's a problem: Only checking roles in FlutterFlow's Conditional Visibility (UI gating) but not in Firestore Security Rules (data gating)
How to avoid: Always implement server-side role checks in Firestore Security Rules in addition to UI Conditional Visibility. The Security Rules are the true security boundary — the UI is just a convenience layer for honest users.
Why it's a problem: Fetching the user role on every page load with a new Firestore query
How to avoid: Fetch the role once on login (or initial app load) and store it in App State. Retrieve it from App State on subsequent pages rather than querying Firestore again. Only re-fetch the role if an admin explicitly changes the user's role.
Why it's a problem: Using string comparison for role checks without a consistent case convention
How to avoid: Use a consistent all-lowercase convention for role values throughout your codebase. Add a note in your Firestore Security Rules file documenting the valid role values: // Valid roles: 'admin', 'editor', 'viewer'.
Why it's a problem: Giving new users no role field, causing role checks to return undefined
How to avoid: Always write the default role ('viewer') to the user document immediately after sign-up in the registration Action Flow. Add a fallback in Security Rules: use data.get('role', 'viewer') to default to 'viewer' if the field is missing.
Best practices
- Always enforce roles in both Firestore Security Rules (server-side) and FlutterFlow Conditional Visibility (UI-side) — the rules are the real security, the visibility is the UX.
- Use a hierarchy where admin inherits all editor permissions and editor inherits all viewer permissions — implement this with isEditor() returning true for both editor and admin roles.
- Store role as a flat string field rather than multiple boolean fields (isAdmin, isEditor) — a single role field is easier to extend and avoids contradictory states like isAdmin: true, isEditor: false.
- Log all role assignment changes to an audit collection with the acting admin's UID, target user UID, old role, and new role — this is critical for compliance and security reviews.
- Test your Security Rules using the Firebase Console's Rules Playground before deploying — verify that viewer-role calls are denied on admin-only operations, not just visually hidden.
- Redirect users who attempt to access role-restricted pages directly by URL — add an On Page Load conditional that navigates away if userRole does not match the required role.
- Consider using Firebase Custom Claims for roles if your app has very high read volume — Custom Claims are attached to the auth token and do not require a Firestore read on each Security Rule check.
- Document your role permissions in a comment at the top of your Firestore Security Rules file — future developers (including yourself) will thank you when debugging access issues.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a FlutterFlow app with Firebase and need role-based access control. I have three roles: admin, editor, and viewer. Please write Firestore Security Rules that: (1) allow viewers to read content documents, (2) allow editors and admins to create and update content, (3) allow only admins to delete content, (4) allow users to update their own user documents but not change their own role field, (5) allow only admins to change any user's role. Use helper functions for isAdmin() and isEditor() that read the role from the user's document. Also include the users collection rules.
I have a FlutterFlow app with a Firestore 'users' collection where each user document has a 'role' field (string: 'admin', 'editor', or 'viewer'). Write the Action Flow logic I need to add to the Home page 'On Page Load' action to: (1) query the users collection for the document where the ID equals the current user's UID, (2) store the role field value in an App State variable called 'userRole'. Describe each action step in the FlutterFlow Action Flow editor, not as code.
Frequently asked questions
Can I have more than three roles in FlutterFlow?
Yes. The role field is a plain string — you can have as many role values as your app needs. Add them to your Firestore Security Rules helper functions and Conditional Visibility conditions. Common extended role sets include: 'superadmin', 'admin', 'moderator', 'editor', 'member', 'guest'. Keep the role hierarchy clearly documented.
What are Firebase Custom Claims and should I use them instead of a Firestore role field?
Firebase Custom Claims are key-value pairs stored in the user's auth token, set via the Firebase Admin SDK. They are accessible in Firestore Security Rules as request.auth.token.role without a get() Firestore read — this is faster and does not count as a billable read. The tradeoff: Custom Claims require a Cloud Function (Blaze plan) to set and update, and take up to 1 hour to propagate after changing. Use the Firestore role field approach (this guide) for most apps, and upgrade to Custom Claims if Firestore read costs become a concern.
How do I prevent users from changing their own role through the FlutterFlow app?
Two layers: (1) In Firestore Security Rules, add a check that rejects updates where the role field is being changed unless the caller is an admin. Use request.resource.data.diff(resource.data).affectedKeys() to check if role is in the changed fields. (2) In FlutterFlow, do not expose the role field in the user's profile settings screen.
Will role checks slow down my app?
The get() function in Firestore Security Rules (used to read the user's role on each request) adds a small latency to each Firestore operation — typically 10-50ms. For most apps this is unnoticeable. If you have high-frequency Firestore operations (real-time listeners, frequent writes), consider upgrading to Firebase Custom Claims to eliminate the extra read on each security check.
What happens to a user's access if I change their role in the admin screen?
The Firestore Security Rules will enforce the new role immediately on the next database operation. However, the App State 'userRole' variable on the user's device still holds the old role until they next reload the app. To propagate role changes instantly, use a Firestore real-time listener on the user's document in the App State and update userRole whenever the document changes.
Can I use this role system for subscription tiers (free vs paid)?
Yes, with a modification. Instead of (or in addition to) a 'role' field, add a 'plan' field with values like 'free', 'pro', 'enterprise'. Apply Conditional Visibility and Security Rules in the same way. Trigger the plan update from a Stripe webhook that calls a Firebase Cloud Function to update the user's plan field in Firestore when a payment succeeds or subscription changes.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation