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

How to Set Up a Subscription Model for Premium Content in FlutterFlow

Create a subscription paywall by storing a subscriptionTier field on each user document. Display premium content with a lock overlay and blurred preview for non-subscribers. Tapping locked content opens a Bottom Sheet with pricing tiers. The Subscribe button redirects to Stripe Checkout, and a Cloud Function webhook updates the user tier on successful payment. Conditional Visibility gates all premium content on both the UI and Firestore Security Rules level.

What you'll learn

  • How to design a Firestore data model for subscription tiers and premium content
  • How to build a paywall Bottom Sheet with pricing comparison
  • How to connect Stripe Checkout for subscription payments via Cloud Function
  • How to gate premium content using Conditional Visibility and Firestore Security Rules
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read25-35 minFlutterFlow Free+Last updated March 2026RapidDev Engineering Team
TL;DR

Create a subscription paywall by storing a subscriptionTier field on each user document. Display premium content with a lock overlay and blurred preview for non-subscribers. Tapping locked content opens a Bottom Sheet with pricing tiers. The Subscribe button redirects to Stripe Checkout, and a Cloud Function webhook updates the user tier on successful payment. Conditional Visibility gates all premium content on both the UI and Firestore Security Rules level.

Quick facts about this guide
FactValue
ToolFlutterFlow
DifficultyBeginner
Time required25-35 min
CompatibilityFlutterFlow Free+
Last updatedMarch 2026

Building a Content Subscription Paywall in FlutterFlow

Subscription models let you monetize content by offering free teasers and locking full access behind a paywall. This tutorial walks through creating a complete subscription system in FlutterFlow with Stripe payments, content gating at both the UI and database level, and a polished paywall experience for non-subscribers.

Prerequisites

  • A FlutterFlow project with Firestore and Firebase Authentication configured
  • A Stripe account with API keys stored in Cloud Function environment variables
  • A content collection in Firestore with at least a few test documents
  • Basic familiarity with FlutterFlow Action Flows

Step-by-step guide

1

Set up the Firestore data model for subscriptions and content

Add a subscriptionTier field (String: 'free', 'monthly', 'annual') and subscriptionExpiry (Timestamp) to your users collection. In your content collection, add an isPremium boolean field. Create a subscription_plans collection with documents for each tier containing: name, priceMonthly, priceAnnual, features (String Array), and stripePriceId. This structure lets you manage plans from Firestore without redeploying the app.

Expected result: Firestore has users with subscriptionTier, content with isPremium, and subscription_plans with Stripe Price IDs.

2

Build the premium content card with lock overlay

Create a ContentCard Component with parameters: title, thumbnailUrl, isPremium, isSubscribed. Use a Stack widget. The bottom layer shows the content thumbnail and title. Add a second layer with Conditional Visibility set to isPremium == true AND isSubscribed == false. This overlay layer is a Container with a semi-transparent black background, a lock Icon centered, and a gradient fade from transparent to dark at the bottom. Below the Stack, show a teaser Text (first 100 characters of content) with a 'Subscribe to continue reading' Text below it, both conditionally visible for non-subscribers.

Expected result: Premium content shows a lock overlay and blurred teaser for non-subscribers, while subscribers see the full content.

3

Create the paywall Bottom Sheet with pricing tiers

Create a PaywallSheet Component. Use a Column with: a headline Text 'Unlock Premium Content', a subtitle Text with the value proposition, then a Row of two Container cards side by side. The left card shows Monthly pricing and the right shows Annual pricing with a 'Save 40%' badge Container positioned in the top-right corner. Each card has: tier name Text, price Text (large headlineMedium), features ListView from subscription_plans query, and a Subscribe Button. Use a Page State variable selectedPlan to track which tier is tapped. Highlight the selected card with a Primary border.

Expected result: A polished pricing Bottom Sheet appears with monthly and annual options, feature lists, and subscribe buttons.

4

Connect Stripe Checkout via Cloud Function

Create a Cloud Function createCheckoutSession that receives userId and stripePriceId, creates a Stripe Checkout Session in subscription mode with success and cancel URLs, and returns the session URL. In FlutterFlow, on the Subscribe Button tap: call the Cloud Function via API Call with the selected plan stripePriceId, then use Launch URL action to open the returned Checkout URL. Create a second Cloud Function as a Stripe webhook listener for checkout.session.completed that reads the customer email, finds the user document, and updates subscriptionTier and subscriptionExpiry.

createCheckoutSession.js
1// Cloud Function: createCheckoutSession
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3
4exports.createCheckoutSession = async (req, res) => {
5 const { userId, priceId } = req.body;
6 const session = await stripe.checkout.sessions.create({
7 mode: 'subscription',
8 payment_method_types: ['card'],
9 line_items: [{ price: priceId, quantity: 1 }],
10 success_url: 'https://yourapp.com/success',
11 cancel_url: 'https://yourapp.com/cancel',
12 metadata: { userId },
13 });
14 res.json({ url: session.url });
15};

Expected result: Tapping Subscribe opens Stripe Checkout. After payment, the webhook updates the user subscriptionTier in Firestore.

5

Gate content with Conditional Visibility and Firestore Security Rules

On every page or Component that displays premium content, wrap the full content in a Container with Conditional Visibility: isPremium == false OR currentUserDocument.subscriptionTier != 'free'. This ensures non-subscribers only see the teaser. Critically, also add Firestore Security Rules that block reads on premium content fields for users without a valid subscription. In rules, check get(/databases/$(database)/documents/users/$(request.auth.uid)).data.subscriptionTier != 'free' before allowing reads on premium documents. UI gating alone is not secure because users can query Firestore directly.

Expected result: Premium content is gated at both the FlutterFlow UI level and the Firestore Security Rules level.

6

Add a subscription badge and manage renewals

On the user profile page, add a Container badge showing the current subscription tier with a colored background (gold for annual, blue for monthly, grey for free). Display subscriptionExpiry as 'Renews on [date]' Text below the badge. Add a Manage Subscription Button that opens the Stripe Customer Portal via a Cloud Function returning the portal URL. For expiry handling, add an On Page Load action that checks if subscriptionExpiry is in the past and subscriptionTier is not free, then calls a Cloud Function to verify the subscription status with Stripe and updates the tier if expired.

Expected result: Users see their subscription badge on their profile and can manage billing through the Stripe Customer Portal.

Complete working example

FlutterFlow Subscription Setup
1FIRESTORE DATA MODEL:
2 users/{uid}
3 subscriptionTier: "free" | "monthly" | "annual"
4 subscriptionExpiry: Timestamp
5 stripeCustomerId: String
6
7 content/{contentId}
8 title: String
9 body: String
10 thumbnailUrl: String
11 isPremium: Boolean
12 category: String
13
14 subscription_plans/{planId}
15 name: "Monthly" | "Annual"
16 priceMonthly: Number
17 priceAnnual: Number
18 features: [String]
19 stripePriceId: String
20
21CONTENT CARD COMPONENT:
22 Stack
23 Column
24 Image (thumbnailUrl)
25 Text (title)
26 Container (lock overlay)
27 Conditional Visibility: isPremium && !isSubscribed
28 Background: semi-transparent black
29 Icon (lock, white, size 40)
30 Text ("Subscribe to unlock")
31
32PAYWALL BOTTOM SHEET:
33 Column
34 Text ("Unlock Premium Content", headlineMedium)
35 Text ("Get unlimited access to all content")
36 Row
37 Container (Monthly card)
38 Text ("Monthly")
39 Text ("$9.99/mo", headlineLarge)
40 ListView (features)
41 Button ("Subscribe Monthly")
42 Container (Annual card + Save badge)
43 Text ("Annual")
44 Text ("$5.99/mo", headlineLarge)
45 ListView (features)
46 Button ("Subscribe Annual")
47 Text ("Cancel anytime", caption)
48
49ACTION FLOW Subscribe Button:
50 1. API Call: createCheckoutSession(userId, stripePriceId)
51 2. Launch URL: response.url
52
53WEBHOOK checkout.session.completed:
54 1. Read metadata.userId
55 2. Update users/{userId}: subscriptionTier, subscriptionExpiry
56
57FIRESTORE SECURITY RULES:
58 match /content/{doc} {
59 allow read: if !resource.data.isPremium
60 || get(/users/$(request.auth.uid)).data.subscriptionTier != 'free';
61 }

Common mistakes

Why it's a problem: Only hiding premium content in the UI without Firestore Security Rules

How to avoid: Add Firestore Security Rules that check the user subscriptionTier before allowing reads on premium content documents.

Why it's a problem: Crediting the subscription tier on the success redirect URL instead of the webhook

How to avoid: Always update the subscriptionTier in the Stripe webhook handler (checkout.session.completed), which is guaranteed to fire on successful payment.

Why it's a problem: Hardcoding Stripe Price IDs in the FlutterFlow app

How to avoid: Store Stripe Price IDs in the subscription_plans Firestore collection. The app reads them dynamically, so you can update pricing without redeploying.

Best practices

  • Gate premium content at both the UI (Conditional Visibility) and database (Firestore Security Rules) levels
  • Use Stripe webhooks for subscription status updates, never client-side success redirects
  • Store Stripe Price IDs in Firestore for dynamic pricing updates without app redeployment
  • Show a compelling teaser (first paragraph + blurred remainder) to convert free users
  • Display subscription expiry and renewal dates clearly on the user profile
  • Provide a Stripe Customer Portal link for users to manage their own billing
  • Check subscription expiry on app load to handle lapsed subscriptions gracefully
  • Include a free trial option in your Stripe Checkout configuration to reduce conversion friction

Still stuck?

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

ChatGPT Prompt

I need to build a subscription paywall in FlutterFlow with Stripe. Show me the Firestore data model for users with subscription tiers, premium content gating with lock overlays, a pricing Bottom Sheet, Stripe Checkout integration via Cloud Function, and Firestore Security Rules that block premium content for non-subscribers.

FlutterFlow Prompt

Create a content feed page where some items have a lock icon overlay. Add a bottom sheet with two pricing cards side by side for monthly and annual subscriptions, each with a subscribe button.

Frequently asked questions

Can I offer a free trial before charging for the subscription?

Yes. When creating the Stripe Checkout Session, add subscription_data.trial_period_days (e.g., 7 or 14). The user enters payment info but is not charged until the trial ends. Update subscriptionTier immediately so they get access during the trial.

How do I handle subscription cancellations?

Listen for the customer.subscription.deleted Stripe webhook event. When received, update the user subscriptionTier to 'free' and clear subscriptionExpiry. Optionally show a 'Your subscription has ended' banner with a resubscribe button.

Can I have more than two subscription tiers?

Yes. Add more documents to subscription_plans in Firestore and create corresponding Stripe Price objects. Adjust your paywall Bottom Sheet layout to accommodate additional plan cards, using a horizontal ListView if you have more than three tiers.

How do I prevent content sharing between subscribers and non-subscribers?

Firestore Security Rules are your enforcement layer. Even if a subscriber shares a direct link, the non-subscriber cannot load the full content because the Security Rules check their subscriptionTier before allowing the read.

Does this work with Apple and Google in-app purchases instead of Stripe?

FlutterFlow supports RevenueCat integration for in-app purchases on iOS and Android. The content gating logic remains the same; only the payment flow changes from Stripe Checkout to the native purchase dialog.

Can RapidDev help implement a full subscription system?

Yes. RapidDev can build the complete subscription flow including Stripe integration, webhook handling, content gating, free trials, upgrade/downgrade logic, and Apple/Google in-app purchase support.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation
Matt Graham

Written by

Matt Graham · CEO & Founder, RapidDev

1,000+ client projects delivered. Columbia University & Harvard Business School alumnus, U.S. Navy veteran. About the author →

Learning is great. Shipping is faster with help.

Our engineers have built 600+ apps on FlutterFlow and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

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.