# How to Build an Affiliate Marketing Platform in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 45-60 min
- Compatibility: FlutterFlow Pro+ (code export required for Secure Storage)
- Last updated: March 2026

## TL;DR

Build an affiliate platform in FlutterFlow with three Firestore collections: affiliates (code, userId, commissionRate), referral_clicks (refCode, timestamp, sessionId), and conversions (affiliateId, orderId, amount, status). Generate unique referral codes, capture the ref URL parameter on app open, store it in Secure Storage to survive page refreshes, and attribute conversions in a Cloud Function that calculates and records commissions.

## Building Referral Tracking That Actually Works

Most affiliate system tutorials show you how to read a URL parameter — and that is only 10% of the problem. The real challenge is attribution: the user clicks a referral link, browses for 20 minutes, and finally signs up. By that time the URL parameter is gone. You need to persist the referral code across navigation, page refreshes, and app restarts. This tutorial builds a complete affiliate pipeline: unique code generation, persistent attribution via Secure Storage, click tracking, conversion recording in a Cloud Function, commission calculation, and a dashboard showing earnings.

## Before you start

- FlutterFlow Pro plan with code export enabled
- Firebase project with Firestore and Authentication configured
- Firebase Cloud Functions with Blaze billing plan
- Basic familiarity with FlutterFlow Custom Actions and App State

## Step-by-step guide

### 1. Design the three Firestore collections for affiliate tracking

In FlutterFlow's Firestore panel, create three collections. The affiliates collection stores one document per affiliate with: userId (String), code (String — unique 8-character code), commissionRate (Number — e.g., 0.10 for 10%), status (String — 'active' or 'suspended'), totalEarnings (Number), totalClicks (Integer), and totalConversions (Integer). The referral_clicks collection logs each link click with: refCode (String), sessionId (String), ipHash (String), userAgent (String), and created_at (Timestamp). The conversions collection records completed purchases with: affiliateId (String), orderId (String), orderAmount (Number), commissionAmount (Number), status (String — 'pending', 'approved', 'paid'), and created_at (Timestamp).

**Expected result:** Three Firestore collections are visible in FlutterFlow's schema editor with all fields typed correctly.

### 2. Generate unique affiliate codes and onboard affiliates

Create a Cloud Function named createAffiliateAccount that accepts a userId and an optional requested code. The function generates a random 8-character alphanumeric code (or validates the requested one for uniqueness), creates the affiliates document, and returns the code to the app. In FlutterFlow, add an 'Apply to Become Affiliate' button on your settings or profile page. When tapped, it calls a Custom Action that invokes the createAffiliateAccount Cloud Function and stores the returned code in the user's Firestore profile. Display the referral link as: https://yourapp.com?ref=YOURCODE in a copyable Text widget.

**Expected result:** After tapping the affiliate application button, the user sees their unique referral URL displayed in a copyable text field on their profile page.

### 3. Capture the ref URL parameter and persist it in Secure Storage

When a new user opens your app via a referral link, you must capture the ref parameter immediately and store it somewhere that survives navigation and page refreshes. In FlutterFlow, create a Custom Action named captureReferralCode. This action reads the current URL's query parameters using Uri.base.queryParameters['ref'] (web) or the initial route (mobile via uni_links package). If a ref code is found, write it to Secure Storage using flutter_secure_storage with the key 'pending_ref_code'. Also log a click event to the referral_clicks collection. Call this action from your app's first page On Page Load event — before any navigation logic runs.

```
// Custom Action: captureReferralCode
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:html' as html show window; // web only

Future<String?> captureReferralCode() async {
  final storage = const FlutterSecureStorage();

  // Check if we already have a pending ref
  final existing = await storage.read(key: 'pending_ref_code');
  if (existing != null) return existing;

  // Parse ref from URL (web)
  String? refCode;
  try {
    final uri = Uri.parse(html.window.location.href);
    refCode = uri.queryParameters['ref'];
  } catch (_) {}

  if (refCode == null || refCode.isEmpty) return null;

  // Verify the code exists
  final doc = await FirebaseFirestore.instance
      .collection('affiliates')
      .doc(refCode.toUpperCase())
      .get();

  if (!doc.exists) return null;

  // Persist to survive navigation
  await storage.write(key: 'pending_ref_code', value: refCode.toUpperCase());

  // Log the click
  await FirebaseFirestore.instance.collection('referral_clicks').add({
    'refCode': refCode.toUpperCase(),
    'created_at': FieldValue.serverTimestamp(),
    'userAgent': html.window.navigator.userAgent,
  });

  // Increment click counter on affiliate doc
  await FirebaseFirestore.instance
      .collection('affiliates')
      .doc(refCode.toUpperCase())
      .update({'totalClicks': FieldValue.increment(1)});

  return refCode.toUpperCase();
}
```

**Expected result:** When the app is opened with ?ref=CODE in the URL, the code is stored in Secure Storage and a click event is logged. Refreshing the page or navigating away does not lose the attribution.

### 4. Record conversions in a Cloud Function at purchase time

When a user completes a purchase (or sign-up, depending on your model), trigger a Cloud Function named recordConversion. This function reads the pending_ref_code from the user's profile or the order document (set it when the order is created from the app state), calculates the commission as orderAmount * commissionRate from the affiliate document, writes a conversion document, increments the affiliate's totalConversions and totalEarnings, and clears the pending referral attribution from the user's profile. Using a Cloud Function here (rather than a direct Firestore write from the app) prevents clients from manually inflating their own commissions.

```
// functions/index.js — recordConversion Cloud Function
const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.recordConversion = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated', 'Login required');
  }

  const { orderId, orderAmount, refCode } = data;

  if (!refCode) return { success: false, reason: 'no_referral' };

  const db = admin.firestore();
  const affiliateRef = db.collection('affiliates').doc(refCode);
  const affiliateDoc = await affiliateRef.get();

  if (!affiliateDoc.exists || affiliateDoc.data().status !== 'active') {
    return { success: false, reason: 'invalid_affiliate' };
  }

  const rate = affiliateDoc.data().commissionRate || 0.10;
  const commission = parseFloat((orderAmount * rate).toFixed(2));

  const batch = db.batch();

  batch.set(db.collection('conversions').doc(), {
    affiliateId: affiliateDoc.id,
    affiliateUserId: affiliateDoc.data().userId,
    referredUserId: context.auth.uid,
    orderId,
    orderAmount,
    commissionAmount: commission,
    status: 'pending',
    created_at: admin.firestore.FieldValue.serverTimestamp(),
  });

  batch.update(affiliateRef, {
    totalConversions: admin.firestore.FieldValue.increment(1),
    totalEarnings: admin.firestore.FieldValue.increment(commission),
  });

  await batch.commit();
  return { success: true, commission };
});
```

**Expected result:** After a purchase, a new conversion document appears in Firestore with the correct commission amount, and the affiliate's totalEarnings field increases accordingly.

### 5. Build the affiliate dashboard page in FlutterFlow

Create a new page in FlutterFlow named AffiliatesDashboard. Query the affiliates collection filtering by userId == currentUser.uid to fetch the current user's affiliate document. Display totalClicks, totalConversions, and totalEarnings as metric cards using Column and Container widgets with large number text. Add a second query on the conversions collection filtered by affiliateId == affiliateDoc.id and ordered by created_at descending. Show each conversion in a ListView with the order date, amount, commission earned, and status badge (Pending, Approved, or Paid). Add a Share button that copies the referral URL to the clipboard using the Clipboard action in FlutterFlow.

**Expected result:** Affiliates can view their referral link, total clicks, conversion count, pending earnings, and a history of all conversions with their commission amounts.

## Complete code example

File: `affiliate_attribution.dart`

```dart
// Custom Action: applyReferralAtSignUp
// Call this immediately after a new user registers
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cloud_functions/cloud_functions.dart';
import 'package:firebase_auth/firebase_auth.dart';

Future<void> applyReferralAtSignUp() async {
  final user = FirebaseAuth.instance.currentUser;
  if (user == null) return;

  // Read the persisted referral code from Secure Storage
  const storage = FlutterSecureStorage();
  final refCode = await storage.read(key: 'pending_ref_code');

  if (refCode == null || refCode.isEmpty) return;

  final db = FirebaseFirestore.instance;

  // Verify affiliate is still active
  final affiliateDoc = await db.collection('affiliates').doc(refCode).get();
  if (!affiliateDoc.exists || affiliateDoc.data()?['status'] != 'active') {
    await storage.delete(key: 'pending_ref_code');
    return;
  }

  // Prevent self-referral
  if (affiliateDoc.data()?['userId'] == user.uid) {
    await storage.delete(key: 'pending_ref_code');
    return;
  }

  // Store referral on the new user's profile for later conversion tracking
  await db.collection('users').doc(user.uid).set(
    {
      'referredBy': refCode,
      'referredAt': FieldValue.serverTimestamp(),
    },
    SetOptions(merge: true),
  );

  // If your model pays on sign-up (not just purchase), record conversion now
  // final callable = FirebaseFunctions.instance.httpsCallable('recordConversion');
  // await callable.call({'orderId': 'signup_${user.uid}', 'orderAmount': 0, 'refCode': refCode});

  // Clear the pending code after attribution
  await storage.delete(key: 'pending_ref_code');
}
```

## Common mistakes

- **Attributing referrals via URL parameter only without persisting to Secure Storage** — URL parameters disappear the moment the user navigates away from the landing page or refreshes. On mobile, deep links lose query parameters after the app launch handoff. Any user who browses before signing up will lose their referral attribution. Fix: On app open, immediately read the ref URL parameter, verify it against Firestore, and write it to flutter_secure_storage. Read from Secure Storage at sign-up time to attribute the conversion correctly.
- **Allowing clients to write directly to the conversions collection** — If your app writes conversion records directly to Firestore, any affiliate could open a browser console and create fake conversions to inflate their earnings. Fix: Only allow conversion records to be written by your Cloud Function. Set Firestore rules to deny direct writes to the conversions collection: 'allow write: if false;' for client access.
- **Not preventing self-referrals in the attribution logic** — Without a self-referral check, an affiliate could create a second account, sign up through their own link, and trigger a commission payment to themselves. Fix: In your attribution action and Cloud Function, compare the affiliate's userId with the new user's uid. If they match, discard the referral code and log the attempt.
- **Storing commissionRate on the conversion document instead of calculating it at conversion time from the affiliates document** — If you change a commission rate after the fact, any historical conversion documents using the old rate create an audit discrepancy. Fix: In the Cloud Function, read the current commissionRate from the affiliates document at conversion time and store both the rate and the calculated amount on the conversion record.

## Best practices

- Persist referral codes in Secure Storage immediately on app open so attribution survives navigation and page refreshes.
- Use Cloud Functions for all commission calculations — never let clients write to conversions or update affiliate earnings directly.
- Implement a self-referral prevention check comparing affiliate userId with the converting user's uid.
- Add a 30-day attribution window: if the pending_ref_code in Secure Storage is older than 30 days, discard it rather than attributing a stale referral.
- Set Firestore rules so only Cloud Functions (via Admin SDK) can write to the conversions collection — clients should have no write access.
- Build an admin review step before paying out commissions — mark conversions as 'approved' only after confirming no fraud indicators.
- Include a fraud detection rule that flags affiliates whose conversion rate exceeds 80% — legitimate referral programs rarely see conversion rates above 30%.

## Frequently asked questions

### Why do I need Secure Storage for referral codes — can I just use App State?

App State is cleared when the app is force-closed or restarted. Secure Storage persists across app restarts and is encrypted on the device. If a user clicks a referral link, opens the app, then closes and reopens it before signing up, App State loses the code but Secure Storage keeps it.

### What is a realistic affiliate commission rate?

Most SaaS affiliate programs pay 20-30% of the first payment or 10-15% recurring. One-time purchase apps typically offer 10-20%. Store the rate on the affiliate document so you can adjust individual affiliate rates without changing your code.

### Can I track affiliate performance for mobile app installs?

Yes, but it requires a mobile deep link setup. Use Firebase Dynamic Links or Branch.io to create trackable install links. When the app is first opened after install, read the referral parameter from the deep link payload and write it to Secure Storage.

### How do I pay affiliates their commissions?

The most common approach is manual Stripe transfers to affiliates' connected Stripe accounts, or using Stripe Payouts. Create a payout_requests collection where affiliates request withdrawals above a minimum threshold. An admin approves and triggers a Stripe transfer from a Cloud Function.

### What happens if two affiliates try to claim the same conversion?

Your attribution logic should use last-click attribution: the most recently stored referral code wins. Since you clear the pending_ref_code from Secure Storage after the first sign-up attribution, subsequent referral attempts by the same user are ignored.

### Do I need FlutterFlow Pro for an affiliate system?

The basic click tracking and Firestore writes can be done on Free plan. However, persisting referral codes to Secure Storage requires the flutter_secure_storage package — a Custom Action that needs code export, which is a Pro plan feature.

### How do I display an affiliate's referral link in the app?

Build the URL dynamically: 'https://yourapp.com?ref=' + affiliateCode. Display it in a Text widget and add a Copy to Clipboard button using FlutterFlow's built-in Clipboard action. For mobile, add a Share button using the share_plus package.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-an-affiliate-marketing-platform-with-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-an-affiliate-marketing-platform-with-flutterflow
