The Flutter Kit logoThe Flutter Kit
Guide

Flutter SaaS Boilerplate — The Complete Guide to Flutter SaaS Templates in 2026

Everything a production Flutter SaaS boilerplate should include — and how to architect subscriptions, teams, and multi-tenancy on Firebase.

Ahmed GaganAhmed Gagan
17 min read

A Flutter SaaS boilerplate is not the same as a generic Flutter starter kit. A SaaS boilerplate assumes you are selling a recurring product — which means subscriptions, user accounts that persist across devices, potentially multi-user teams, entitlement gating, an admin surface, and a billing backend. It is a meaningful step up in complexity from "auth + paywall."

I have shipped and consulted on several Flutter SaaS products. This guide is the architectural opinion I wish someone had handed me: what a Flutter SaaS boilerplate should include, how to design the data model for multi-tenancy, how to wire billing without getting paged at 2am, and which existing kits actually deliver this vs. just marketing it.

SaaS Boilerplate vs Generic Flutter Boilerplate

Here is the honest delta. A generic Flutter boilerplate gives you the plumbing. A SaaS boilerplate gives you the business model.

FeatureGeneric Flutter BoilerplateFlutter SaaS Boilerplate
AuthYesYes + email verification + password reset
SubscriptionsRevenueCat paywallRevenueCat + Stripe web + entitlement sync
User profilesBasicProfiles + preferences + sessions
Multi-user accountsNoTeam accounts + roles
InvitationsNoEmail invites + magic links
Admin dashboardNoYes (web-first)
Usage meteringNoPer-user / per-team counters
Webhooks handlerNoYes (RevenueCat + Stripe)
Cross-platform billingMobile onlyMobile + Web
Role-based permissionsNoOwner / Admin / Member / Viewer

What Makes a Flutter App "SaaS"?

Four characteristics. If your product has all four, you need a SaaS boilerplate, not a generic kit.

  1. Recurring revenue — subscriptions (monthly/yearly), not one-time purchases
  2. Persistent user state — data that belongs to a user and syncs across devices (mobile + web)
  3. Gated features — entitlements that unlock capability based on plan tier
  4. Server-side truth — the backend, not the client, decides what a user can do

Examples: a note-taking app with cloud sync and a pro tier. A fitness app with AI coaching behind a paywall. A project management tool for small teams. A CRM. A journaling app with team accounts. All SaaS. A one-time puzzle game is not.

The SaaS Architecture (Flutter + Firebase + RevenueCat + Stripe)

Here is the reference stack I recommend for a solo or small-team Flutter SaaS in 2026. Every piece is production-proven.

LayerTechnologyWhy
Client (mobile)Flutter 3.24+ / Dart 3iOS + Android from one codebase
Client (web)Flutter Web OR Next.jsMarketing + admin + billing pages
State managementBLoC + get_itTestable, scales to complex SaaS flows
AuthFirebase AuthEmail, Apple, Google, magic links
DatabaseFirestoreReal-time sync, offline, security rules
Mobile billingRevenueCatApp Store + Play Store unified
Web billingStripe CheckoutCredit cards, no 30% Apple tax
Entitlement source of truthFirestore /users/{uid}/entitlementsUnified across RevenueCat + Stripe
Backend logicFirebase Cloud Functions (v2)Webhooks, admin actions, usage metering
AnalyticsPostHogFunnel analysis, feature flags, A/B testing

Firestore Schema for Multi-User SaaS

The critical decision in a Flutter SaaS is: single-user or team-based? A journaling app might be single-user (each account is one person). A project management tool is team-based (an account is an organization with multiple members). The schema differs significantly.

Single-User SaaS Schema

users/{userId}
  ├── profile: { name, email, createdAt, ... }
  ├── entitlements: { plan: 'pro', expiresAt, source: 'revenuecat' }
  ├── preferences: { theme, notifications, ... }
  ├── usage/{period}            (subcollection — monthly counters)
  │     └── { aiCallsUsed, storageBytesUsed, ... }
  └── ...feature-specific data (notes, habits, etc.)

Team-Based SaaS Schema

users/{userId}
  ├── profile
  └── memberships: [{ teamId, role }]

teams/{teamId}
  ├── profile: { name, ownerUid, plan, ... }
  ├── entitlements: { plan, seats, expiresAt, source }
  ├── members/{userId}: { role: 'owner'|'admin'|'member'|'viewer', joinedAt }
  ├── invites/{inviteId}: { email, role, token, expiresAt }
  ├── usage/{period}
  └── ...shared data (projects, documents, etc.)

The team-based schema puts shared data under teams/{teamId} instead of users/{uid}. This is the most important design decision in a team SaaS — get it right on day one because migrating later is brutal.

Entitlements: The Single Source of Truth

The mistake I see constantly: checking RevenueCat on mobile and Stripe on web independently, with no unified view. The fix is an entitlements document in Firestore that both services write to via webhooks. Your app reads one place, period.

// functions/src/webhooks.ts — unify RevenueCat and Stripe into Firestore
import * as functions from 'firebase-functions/v2/https';
import { getFirestore } from 'firebase-admin/firestore';

export const revenueCatWebhook = functions.onRequest(async (req, res) => {
  // Verify RevenueCat signature here (omitted for brevity)
  const event = req.body.event;
  const userId = event.app_user_id;
  const entitlement = event.entitlement_ids?.[0];
  const expiresAt = new Date(event.expiration_at_ms);

  await getFirestore().doc(`users/${userId}/entitlements/active`).set({
    plan: entitlement ?? 'free',
    expiresAt,
    source: 'revenuecat',
    updatedAt: new Date(),
  }, { merge: true });

  res.status(200).send('ok');
});

export const stripeWebhook = functions.onRequest(async (req, res) => {
  // Verify Stripe signature here
  const event = req.body;
  if (event.type === 'customer.subscription.updated') {
    const sub = event.data.object;
    const userId = sub.metadata.userId;
    await getFirestore().doc(`users/${userId}/entitlements/active`).set({
      plan: sub.items.data[0].price.lookup_key,
      expiresAt: new Date(sub.current_period_end * 1000),
      source: 'stripe',
      updatedAt: new Date(),
    }, { merge: true });
  }
  res.status(200).send('ok');
});

Now your Flutter app just streams users/{uid}/entitlements/active and displays the current plan. Upgrades on mobile (RevenueCat) and web (Stripe) converge to the same document.

The Paywall Pattern

On mobile, show a RevenueCat paywall. On web, redirect to a Stripe Checkout session. Gate every premium feature with a single helper function.

// lib/features/billing/entitlement_service.dart
class EntitlementService {
  final FirebaseFirestore _db;
  final FirebaseAuth _auth;

  Stream<Entitlement> watch() {
    return _auth.userChanges().switchMap((user) {
      if (user == null) return Stream.value(Entitlement.free());
      return _db
          .doc('users/${user.uid}/entitlements/active')
          .snapshots()
          .map((snap) => Entitlement.fromFirestore(snap));
    });
  }

  bool has(Entitlement e, String feature) {
    if (e.plan == 'pro' || e.plan == 'team') return true;
    return _freeFeatures.contains(feature);
  }

  static const _freeFeatures = {'basic_notes', 'one_project'};
}

Every premium feature in your Flutter app wraps its gate in entitlement.has('ai_chat'). If false, show the paywall. Clean, testable, and identical across mobile and web builds.

Team Accounts and Invitations

If you are building a team SaaS, the invitation flow is deceptively hard. Here is the pattern:

  1. Owner creates an invite: Cloud Function writes teams/{teamId}/invites/{inviteId} with a random token + expiry
  2. Cloud Function sends an email via SendGrid with a deep link: yourapp://invite/{token}
  3. Invitee opens the link — if signed in, they accept; if not, they sign up first, then accept
  4. Accepting an invite: Cloud Function validates the token, creates teams/{teamId}/members/{uid} with the invited role, deletes the invite doc
  5. Roles enforced via Firestore security rules: owner/admin can write, member can read/write scoped data, viewer is read-only
// Firestore rules for team-based SaaS
match /teams/{teamId} {
  allow read: if isTeamMember(teamId);
  allow update: if isTeamAdmin(teamId);

  match /projects/{projectId} {
    allow read: if isTeamMember(teamId);
    allow write: if isTeamMember(teamId) &&
                    !isTeamViewer(teamId);
  }

  function isTeamMember(teamId) {
    return exists(/databases/$(database)/documents/teams/$(teamId)/members/$(request.auth.uid));
  }
  function isTeamAdmin(teamId) {
    let m = get(/databases/$(database)/documents/teams/$(teamId)/members/$(request.auth.uid));
    return m.data.role in ['owner', 'admin'];
  }
  function isTeamViewer(teamId) {
    let m = get(/databases/$(database)/documents/teams/$(teamId)/members/$(request.auth.uid));
    return m.data.role == 'viewer';
  }
}

Admin Dashboard

Every SaaS needs an admin surface for support and operations. Two approaches:

  • Firebase-only: Build a simple admin route in Flutter Web gated by a custom claim. Good enough for MVP.
  • Separate Next.js admin app: Cleaner separation, richer tooling, better for $10K+ MRR products.

Minimum admin views you need: find user by email, see their entitlements + subscription source, grant/revoke an entitlement manually, view recent usage, impersonate for support (with audit log). Build these on day one — customer support time saved pays for itself in week one.

Usage Metering

If your SaaS has usage-based limits (AI calls, storage, API requests), meter on the server, not the client. Cloud Functions increments a counter; Firestore rules enforce the cap.

// functions/src/ai.ts
export const aiChat = functions.onCall(async (request) => {
  const uid = request.auth?.uid;
  if (!uid) throw new functions.HttpsError('unauthenticated', '');

  const period = new Date().toISOString().slice(0, 7); // YYYY-MM
  const usageRef = db.doc(`users/${uid}/usage/${period}`);
  const ent = await db.doc(`users/${uid}/entitlements/active`).get();
  const plan = ent.data()?.plan ?? 'free';
  const limit = plan === 'pro' ? 1000 : 20;

  const snap = await usageRef.get();
  const used = snap.data()?.aiCalls ?? 0;
  if (used >= limit) {
    throw new functions.HttpsError('resource-exhausted', 'quota');
  }

  await usageRef.set({ aiCalls: FieldValue.increment(1) }, { merge: true });
  // ...forward to OpenAI
});

Why The Flutter Kit Is a Strong Flutter SaaS Starter

The Flutter Kit was designed with SaaS in mind even though it is not marketed purely as a SaaS kit. You get Firebase Auth with all sign-in methods, Firestore wired up with user-scoped security rules, RevenueCat with entitlement checks abstracted into a reusable service, Cloud Functions configured, a Material 3 design system, and BLoC architecture with repository interfaces that make swapping implementations trivial.

What you add on top for a full SaaS: team-based schema (if needed), Stripe web billing + unified entitlement sync, the admin dashboard, and team invitations. The Flutter Kit gets you ~70% of the way there, and the repository pattern means the extensions stay clean.

At $69 one-time for unlimited commercial apps, it is the cheapest path to a production Flutter SaaS foundation. Grab it on the checkout page or browse the features to see the full list.

SaaS Launch Checklist

  • Auth with email verification + password reset
  • Entitlement doc in Firestore — single source of truth
  • RevenueCat wired for mobile, Stripe for web
  • Both billing sources write to the same entitlement doc via webhooks
  • Team schema (if multi-user) with roles and rules
  • Invitation flow with email + deep link
  • Usage metering enforced server-side
  • Admin surface for support (find user, grant entitlement, impersonate)
  • Delete-account flow (Apple + GDPR compliance)
  • Data export (GDPR requirement for EU users)
  • Analytics funnel from signup to first value to subscription
  • Trial logic (RevenueCat introductory offer + Stripe trial period)

Tick every box and you have a real Flutter SaaS foundation. Skip any of them and you will ship a "prototype with a paywall" instead of a real product. Pick a starter that handles the plumbing and focus your time on what makes your SaaS actually different.

Share this article

Ready to ship your Flutter app faster?

The Flutter Kit gives you a production-ready Flutter codebase with onboarding, paywalls, auth, AI integrations, and more. Stop building boilerplate. Start building your product.

Get The Flutter Kit