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

How to Set Up a Tiered Access Control System in FlutterFlow

Implement subscription-based access control by storing a tier field (free, basic, pro, enterprise) on each user document. Gate features using Conditional Visibility that checks whether the user's tier meets or exceeds the required tier for each feature. Locked features show an upgrade CTA with a tier comparison matrix. Stripe subscription purchases trigger a webhook Cloud Function that updates the user's tier, and a scheduled function handles downgrades when subscriptions expire.

What you'll learn

  • How to store and check subscription tiers on user documents
  • How to gate features with Conditional Visibility based on tier levels
  • How to build a tier comparison matrix showing feature availability
  • How to connect Stripe subscriptions to tier upgrades via webhook
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read20-25 minFlutterFlow Pro+ (Cloud Functions required for Stripe webhooks)March 2026RapidDev Engineering Team
TL;DR

Implement subscription-based access control by storing a tier field (free, basic, pro, enterprise) on each user document. Gate features using Conditional Visibility that checks whether the user's tier meets or exceeds the required tier for each feature. Locked features show an upgrade CTA with a tier comparison matrix. Stripe subscription purchases trigger a webhook Cloud Function that updates the user's tier, and a scheduled function handles downgrades when subscriptions expire.

Building Tiered Access Control in FlutterFlow

Tiered access control lets you offer different feature sets to free, basic, pro, and enterprise users. Unlike role-based access (which defines what actions users can perform), tiered access defines how much of your app users can access based on their subscription level. This tutorial builds the complete flow: tier checking, feature gating with lock overlays, an upgrade page with a comparison matrix, and Stripe-powered tier management.

Prerequisites

  • A FlutterFlow project on the Pro plan or higher
  • Firebase project with Firestore, Cloud Functions, and Authentication enabled
  • A Stripe account with subscription products created (one per tier)
  • Basic familiarity with Conditional Visibility and Firestore in FlutterFlow

Step-by-step guide

1

Add tier fields to the user document and create the features collection

Add two fields to your users collection: tier (String — free, basic, pro, or enterprise) and subscriptionExpiresAt (Timestamp). Set the default tier to 'free' for new signups by adding a Firestore Create Document action in your sign-up flow. Create a `features` collection with documents for each gated feature, containing: name (String), description (String), requiredTier (String), and icon (String — icon name). Create an Option Set named SubscriptionTier with values ordered by access level: free (0), basic (1), pro (2), enterprise (3). The numeric ordering lets you compare tiers easily in Custom Functions.

Expected result: Every user has a tier field, and each feature has a requiredTier that can be compared against the user's tier.

2

Create a Custom Function to compare tier levels

Create a Custom Function named tierMeetsRequirement that takes two String parameters: userTier and requiredTier. The function maps each tier to a numeric level (free=0, basic=1, pro=2, enterprise=3) and returns true if the user's tier level is greater than or equal to the required tier level. This single function powers all tier checks throughout the app. Use it in Conditional Visibility conditions, Action Flow conditions, and Backend Query filters to control access consistently.

tier_meets_requirement.dart
1// Custom Function: tierMeetsRequirement
2bool tierMeetsRequirement(
3 String userTier,
4 String requiredTier,
5) {
6 const levels = {
7 'free': 0,
8 'basic': 1,
9 'pro': 2,
10 'enterprise': 3,
11 };
12 final userLevel = levels[userTier] ?? 0;
13 final requiredLevel = levels[requiredTier] ?? 0;
14 return userLevel >= requiredLevel;
15}

Expected result: The function returns true when the user's tier meets or exceeds the requirement, and false when they need to upgrade.

3

Gate features with lock overlays and upgrade CTAs

For each gated feature section on your app pages, wrap the content in a Stack. The bottom layer is the actual feature content. The top layer is a Container overlay with Conditional Visibility set to show when tierMeetsRequirement returns false (user tier is below required tier). Style the overlay with a semi-transparent white background, a lock Icon at the center, the required tier name (e.g., 'Pro Feature'), and an 'Upgrade' ElevatedButton that navigates to the Pricing page. This creates a frosted-glass lock effect over features the user cannot access while still showing a preview of what they are missing.

Expected result: Features above the user's tier show a lock overlay with an upgrade button. Features within the tier are fully accessible.

4

Build the tier comparison matrix on the pricing page

Create a Pricing page with a comparison matrix. Use a Column containing: a header Row with tier names (Free, Basic, Pro, Enterprise), and then one Row per feature from the features collection (Backend Query ordered by name). Each Row shows the feature name on the left and four cells — one per tier. Each cell shows a checkmark Icon if the feature's requiredTier level is less than or equal to that column's tier level (using the tierMeetsRequirement function), or an empty cell otherwise. At the bottom of each tier column, add a Subscribe button with the monthly price. The current user's tier column is highlighted with a colored border and 'Current Plan' label.

Expected result: Users see a clear matrix of which features each tier unlocks. Their current plan is highlighted, and they can subscribe to upgrade.

5

Connect Stripe subscriptions to tier updates via webhook

Create Stripe subscription products for each tier in the Stripe Dashboard (basic, pro, enterprise). Create a Cloud Function that generates a Stripe Checkout Session for subscription mode, accepting the priceId parameter. On the FlutterFlow pricing page, each Subscribe button calls this function with the corresponding priceId and opens the returned URL. Create a second Cloud Function as a Stripe webhook handler. On the checkout.session.completed event, read the subscription data, determine which tier was purchased based on the priceId, and update the user's Firestore document with the new tier and subscriptionExpiresAt. On customer.subscription.deleted, revert the user's tier to 'free'.

stripeWebhook.js
1// Stripe webhook handler (simplified)
2exports.stripeWebhook = functions.https
3 .onRequest(async (req, res) => {
4 const sig = req.headers['stripe-signature'];
5 const event = stripe.webhooks.constructEvent(
6 req.rawBody, sig, webhookSecret
7 );
8
9 if (event.type === 'checkout.session.completed') {
10 const session = event.data.object;
11 const userId = session.client_reference_id;
12 const priceId = session.line_items?.data[0]
13 ?.price?.id;
14
15 const tierMap = {
16 'price_basic_xxx': 'basic',
17 'price_pro_xxx': 'pro',
18 'price_enterprise_xxx': 'enterprise',
19 };
20 const tier = tierMap[priceId] || 'free';
21
22 await admin.firestore()
23 .doc(`users/${userId}`)
24 .update({ tier, subscriptionExpiresAt: ... });
25 }
26
27 if (event.type === 'customer.subscription.deleted') {
28 // Revert to free tier
29 const userId = /* extracted from metadata */;
30 await admin.firestore()
31 .doc(`users/${userId}`)
32 .update({ tier: 'free' });
33 }
34
35 res.status(200).send('OK');
36 });

Expected result: When a user completes a Stripe subscription, their tier updates automatically. When they cancel, their tier reverts to free.

6

Handle tier expiration and enforce access server-side

Create a scheduled Cloud Function that runs daily and queries users where subscriptionExpiresAt is in the past and tier is not 'free'. For each expired user, update their tier to 'free'. This catches edge cases where the Stripe webhook did not fire. Additionally, add Firestore Security Rules that enforce tier-based access on sensitive collections. For example, a rule on a premium-only collection can check request.auth.token.tier (set via Custom Claims from a Cloud Function) to reject read/write operations from free-tier users even if the UI is bypassed.

Expected result: Expired subscriptions are caught by the scheduled cleanup function, and Firestore rules enforce tier access at the database level.

Complete working example

tierMeetsRequirement Custom Function + Webhook
1// Custom Function: tierMeetsRequirement
2// Returns true if userTier >= requiredTier
3
4bool tierMeetsRequirement(
5 String userTier,
6 String requiredTier,
7) {
8 const tierLevels = {
9 'free': 0,
10 'basic': 1,
11 'pro': 2,
12 'enterprise': 3,
13 };
14
15 final userLevel = tierLevels[userTier] ?? 0;
16 final requiredLevel = tierLevels[requiredTier] ?? 0;
17 return userLevel >= requiredLevel;
18}
19
20// Custom Function: tierDisplayName
21// Returns a user-friendly label for the tier
22
23String tierDisplayName(String tier) {
24 const names = {
25 'free': 'Free',
26 'basic': 'Basic',
27 'pro': 'Pro',
28 'enterprise': 'Enterprise',
29 };
30 return names[tier] ?? 'Free';
31}
32
33// Custom Function: tierColor
34// Returns the hex color for tier badge styling
35
36String tierColor(String tier) {
37 const colors = {
38 'free': '#9E9E9E',
39 'basic': '#2196F3',
40 'pro': '#FF9800',
41 'enterprise': '#9C27B0',
42 };
43 return colors[tier] ?? '#9E9E9E';
44}
45
46// Firestore Security Rules (reference)
47// rules_version = '2';
48// service cloud.firestore {
49// match /databases/{database}/documents {
50// match /premium_content/{docId} {
51// allow read: if request.auth != null
52// && request.auth.token.tier in ['pro', 'enterprise'];
53// }
54// }
55// }
56
57// Usage in FlutterFlow:
58// Conditional Visibility on a Container:
59// tierMeetsRequirement(currentUserTier, 'pro') == false
60// This shows the lock overlay when the user is below Pro.

Common mistakes

Why it's a problem: Only checking the tier on page load instead of on every sensitive action

How to avoid: Check tier on every sensitive action trigger (button taps, form submissions). Use real-time Firestore listeners on the user document so tier changes propagate immediately to the UI.

Why it's a problem: Only hiding UI elements without enforcing tier in Firestore Security Rules

How to avoid: Add Firestore Security Rules that check the user's tier via Custom Claims. UI gating provides a good experience; server-side rules provide actual security.

Why it's a problem: Hardcoding tier prices in the comparison matrix instead of reading from Stripe

How to avoid: Store pricing data in a Firestore config document that a Cloud Function updates from Stripe product data. The comparison matrix reads from this config document.

Best practices

  • Use a single tierMeetsRequirement Custom Function for all tier checks to ensure consistent logic throughout the app
  • Show a preview of locked features with a frosted overlay rather than hiding them completely — this motivates upgrades
  • Highlight the user's current tier in the comparison matrix with a colored border and 'Current Plan' label
  • Use Firestore Custom Claims for server-side tier enforcement in Security Rules
  • Handle subscription downgrades gracefully by showing a 'Your access expires on [date]' message
  • Add a tier badge to the user's profile to provide social proof of their subscription level
  • Test the full upgrade and downgrade flow in Stripe test mode before going live

Still stuck?

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

ChatGPT Prompt

I'm building a tiered access control system in FlutterFlow with tiers: free, basic, pro, enterprise. I need a Custom Function to compare tiers, Conditional Visibility to gate features with lock overlays, a pricing comparison matrix, and Stripe subscription webhooks to update user tiers. Walk me through the full implementation.

FlutterFlow Prompt

Create a pricing page with a feature comparison table showing which features are available in Free, Basic, Pro, and Enterprise tiers. Add checkmarks for available features and lock icons for unavailable ones. Include Subscribe buttons under each tier that open a Stripe checkout.

Frequently asked questions

What is the difference between role-based access and tiered access?

Role-based access controls what a user can DO (admin can delete, editor can edit, viewer can read). Tiered access controls how much a user can ACCESS based on their subscription level (free gets 3 features, pro gets 10). You can use both together.

How do I handle users who downgrade from Pro to Basic?

When the Stripe webhook fires for a downgrade, update the user's tier to the new level. On their next page load or action, the tier check will show lock overlays on features they no longer have access to. Show a friendly message explaining what they have lost.

Can I offer a free trial of a higher tier?

Yes. Use Stripe's built-in trial period feature when creating the subscription. Set the user's tier to the trial tier immediately and set subscriptionExpiresAt to the trial end date. The scheduled cleanup function reverts their tier if they do not convert.

How do I prevent users from sharing Pro accounts?

Track active sessions per user. If a user has more than a defined number of concurrent sessions, force logout on the oldest session. Store session tokens in Firestore and validate on each page load.

Can I add usage-based limits within tiers (e.g., 100 API calls for Basic)?

Yes. Add a usage counter field on the user document. Increment it with each action via a Cloud Function. Check the counter against the tier's limit before allowing the action. Reset counters monthly with a scheduled function.

Can RapidDev help implement a complete SaaS subscription system?

Yes. RapidDev can build full SaaS tier management with Stripe billing, usage metering, team seats, annual discounts, coupon codes, and admin analytics dashboards.

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.