The official Flutter SDK for Insert Affiliate - track affiliate-driven in-app purchases and reward your partners automatically.
What does this SDK do? It connects your Flutter app to Insert Affiliate's platform, enabling you to track which affiliates drive subscriptions and automatically pay them commissions when users make in-app purchases.
Future<void> _makePurchase(AdaptyPaywallProduct product) async {
// Always ensure affiliate ID is set before purchasefinal affiliateId = await insertAffiliateSdk.returnInsertAffiliateIdentifier();
if (affiliateId != null && affiliateId.isNotEmpty) {
final builder = AdaptyProfileParametersBuilder()
..setCustomStringAttribute(affiliateId, 'insert_affiliate');
awaitAdapty().updateProfile(builder.build());
}
// Now make the purchasefinal result = awaitAdapty().makePurchase(product: product);
// Handle result...
}
import'package:app_links/app_links.dart';
import'package:insert_affiliate_flutter_sdk/insert_affiliate_flutter_sdk.dart';
voidmain() async {
WidgetsFlutterBinding.ensureInitialized();
insertAffiliateSdk = InsertAffiliateFlutterSDK(
companyCode: "YOUR_COMPANY_CODE",
insertLinksEnabled: true,
insertLinksClipboardEnabled: true,
);
// Set up callback for affiliate identifier changes
insertAffiliateSdk.setInsertAffiliateIdentifierChangeCallback((identifier, offerCode) async {
if (identifier != null) {
// For RevenueCat:// var attrs = {"insert_affiliate": identifier, "insert_timedout": ""};// if (offerCode != null) attrs["affiliateOfferCode"] = offerCode;// await Purchases.setAttributes(attrs);// await Purchases.syncAttributesAndOfferingsIfNeeded();// For Adapty:// final builder = AdaptyProfileParametersBuilder()// ..setCustomStringAttribute(identifier, 'insert_affiliate');// await Adapty().updateProfile(builder.build());// For Apphud:// await Apphud.setUserProperty(key: "insert_affiliate", value: identifier, setOnce: false);
}
});
// Set up deep link listener_setupDeepLinkListener();
runApp(MyApp());
}
void_setupDeepLinkListener() async {
final appLinks = AppLinks();
// Check for initial linkfinal initialLink = await appLinks.getInitialLink();
if (initialLink != null) {
await insertAffiliateSdk.handleDeepLink(initialLink.toString());
}
// Listen for incoming links
appLinks.uriLinkStream.listen((Uri uri) async {
await insertAffiliateSdk.handleDeepLink(uri.toString());
});
}
Universal Links (Optional, Recommended)
Universal Links provide a better user experience than custom URL schemes. When a user taps an Insert Link and already has your app installed, iOS opens the app directly — without loading the browser.
Prerequisites:
Enter your Apple Team ID and iOS Bundle Identifier in the Insert Affiliate dashboard settings
Step 1: Add Associated Domains in Xcode
Go to your app target → Signing & Capabilities → + Capability → Associated Domains.
Add:
applinks:insertaffiliate.link
If you have a custom domain (e.g. links.yourcompany.com), also add:
applinks:links.yourcompany.com
Step 2: Disable Flutter's built-in deep link handling
Add this to your ios/Runner/Info.plist to prevent Flutter's engine from trying to route Universal Link URLs (which causes Safari to open after the SDK handles the link):
<key>FlutterDeepLinkingEnabled</key><false/>
Step 3: Handle Universal Links in your app
The app_links package used above already handles Universal Links — both getInitialLink() and uriLinkStream receive Universal Link URLs. The SDK's handleDeepLink() method routes them to the correct handler automatically. No additional code changes needed.
Note: Universal Links won't trigger if you type the URL directly into Safari's address bar — tap it from Notes or Messages for a real test.
Testing Universal Links:
# iOS Simulator
xcrun simctl openurl booted "https://insertaffiliate.link/YOUR_COMPANY_CODE/TEST_SHORT_CODE"# Real device (get UDID from: xcrun devicectl list devices)
xcrun devicectl device process launch -d YOUR_DEVICE_UDID com.apple.mobilesafari "https://insertaffiliate.link/YOUR_COMPANY_CODE/TEST_SHORT_CODE"
Android App Links (Optional, Recommended)
Android App Links provide a better user experience than custom URL schemes. When a user taps an Insert Link and already has your app installed, Android opens the app directly — without loading the browser or showing a disambiguation dialog.
Prerequisites:
Enter your Android Bundle Identifier (package name) and SHA-256 Certificate Fingerprints in the Insert Affiliate dashboard settings
Step 1: Add intent filter to AndroidManifest.xml
Add this inside your main <activity> tag in android/app/src/main/AndroidManifest.xml:
[ ] SDK Initializes: Check console for SDK initialized with company code log
[ ] Affiliate Identifier Stored: Click a test affiliate link and verify identifier is stored
[ ] Purchase Tracked: Make a test purchase and verify it appears in Insert Affiliate dashboard
Testing Commands
# Test deep link (Android Emulator)
adb shell am start -W -a android.intent.action.VIEW -d "https://your-deep-link-url/abc123"# Test deep link (iOS Simulator)
xcrun simctl openurl booted "https://your-deep-link-url/abc123"
Check Stored Affiliate Identifier
final affiliateId = await insertAffiliateSdk.returnInsertAffiliateIdentifier();
print('Current affiliate ID: $affiliateId');
Common Setup Issues
Issue
Solution
"Company code is not set"
Ensure SDK is initialized before calling other methods
"No affiliate identifier found"
User must click an affiliate link before making a purchase
Deep link opens browser instead of app
Verify URL schemes in Info.plist (iOS) and AndroidManifest.xml (Android)
Purchase not tracked
Check webhook configuration in IAP verification platform
🔧 Advanced Features
Event Tracking (Beta)
Track custom events beyond purchases to incentivize affiliates for specific actions.
Automatically apply discounts or trials when users come from specific affiliates.
How It Works:
Configure an offer code modifier in your dashboard (e.g., oneWeekFree)
SDK automatically fetches and stores the modifier when affiliate identifier is set
The affiliateOfferCode attribute is set in RevenueCat for targeting
Option 1: RevenueCat Targeting (Recommended)
Use RevenueCat's targeting feature to automatically show different offerings based on the affiliateOfferCode attribute. No manual product ID construction needed.
Step 1: Create offerings in RevenueCat (e.g., default and oneWeekFree)
Step 2: Configure targeting rules in RevenueCat:
Condition: affiliateOfferCode is any of oneWeekFree
Show Offering: Select your promotional offering
Step 3: Just use offerings.current in your app:
final offerings = awaitPurchases.getOfferings();
final packages = offerings.current?.availablePackages ?? [];
// RevenueCat targeting automatically shows the right offering!
Option 2: Manual Product ID Construction (Alternative)
If not using RevenueCat targeting, you can manually construct product IDs:
Control how long affiliate attribution remains active.
Set Timeout During Initialization:
insertAffiliateSdk = InsertAffiliateFlutterSDK(
companyCode: "YOUR_COMPANY_CODE",
attributionTimeout: 604800, // 7 days in seconds
);
Runtime Updates:
// Set 7-day timeoutawait insertAffiliateSdk.setAffiliateAttributionTimeout(604800);
// Disable timeout (never expires)await insertAffiliateSdk.setAffiliateAttributionTimeout(0);
// Check if attribution is still validfinal isValid = await insertAffiliateSdk.isAffiliateAttributionValid();
// Get when attribution was storedfinal storedDate = await insertAffiliateSdk.getAffiliateStoredDate();
Common Timeout Values:
1 day: 86400
7 days: 604800 (recommended)
30 days: 2592000
No timeout: 0 (default)
Bypass Timeout for Testing:
// Get identifier even if attribution has expiredfinal identifier = await insertAffiliateSdk.returnInsertAffiliateIdentifier(ignoreTimeout: true);
Affiliate Change Callback
Get notified when the affiliate identifier changes. The callback now includes both the identifier and the offer code:
insertAffiliateSdk.setInsertAffiliateIdentifierChangeCallback((identifier, offerCode) async {
if (identifier != null) {
print('Affiliate changed: $identifier, offer code: $offerCode');
// Update your IAP platform with both identifier and offer codevar attributes = {"insert_affiliate": identifier, "insert_timedout": ""};
if (offerCode != null) {
attributes["affiliateOfferCode"] = offerCode;
}
awaitPurchases.setAttributes(attributes);
awaitPurchases.syncAttributesAndOfferingsIfNeeded();
}
});
Prevent Affiliate Transfer
Protect the original affiliate from being overwritten by subsequent affiliate links:
When enabled, if a user already has an affiliate identifier stored, any new affiliate links will be blocked. This ensures the original affiliate who referred the user gets credit for any future purchases.
Get the Unix timestamp (in milliseconds) when the current affiliate attribution expires:
final expiryTimestamp = await insertAffiliateSdk.getAffiliateExpiryTimestamp();
if (expiryTimestamp != null) {
final expiryDate = DateTime.fromMillisecondsSinceEpoch(expiryTimestamp);
print('Attribution expires at: $expiryDate');
// Check if expiredif (DateTime.now().millisecondsSinceEpoch > expiryTimestamp) {
print('Attribution has expired');
}
}
Returns null if no attribution date is stored or if timeout is disabled.
In-App Referrals (Refer a Friend)
Turn your own users into affiliates from inside your app. Each referrer is a normal Insert Affiliate affiliate (same dashboard, commission and payouts), and your app can read how many referrals they have made to reward them.
Switch the program on in the Insert Affiliate dashboard first. Enrolment is refused while it is off.
Drop-in screen:
await insertAffiliateSdk.showReferAFriend(
context,
options: ReferAFriendOptions(
email: currentUser.email, // prefill with your logged-in user
name: currentUser.name,
appUserId: revenueCatAppUserId, // optional, for automatic rewards (see below)
shareMessage: 'Get a free week of MyApp: {link}', // optional, supports {link} and {code}
primaryColor: Colors.teal, // optional, overrides the dashboard colour
headline: 'Invite friends', // optional, overrides the dashboard copy
rewardText: 'Earn a free week for every friend who subscribes.',
onClose: () => refreshRewards(),
),
);
The screen handles every step: loading, the "Get my link" form, the 6-digit email code when the email is already an affiliate, and the enrolled view with the code, link, Copy and Share buttons, referral count, amount earned and an "Open my dashboard" link. When the referrer has been granted rewards it also shows "Free premium until {date}" (while that date is in the future) a "Your rewards" list of the codes this phone can redeem, each with a Redeem button: App Store offer codes on iOS, Google Play promo codes on Android. You can also push or embed ReferAFriendScreen(sdk: insertAffiliateSdk, options: ...) yourself.
Headline, reward text and colour come from your options first, then your dashboard settings, then the defaults ("Refer a friend", #6A0DAD), so you can change the wording without an app release. fontFamily and cornerRadius are also available.
Translating or rewording the screen: pass strings. Every field is optional, and one left out or blank keeps the English default below. {email} and {date} are replaced when the screen shows the text, so keep them in a translation.
await insertAffiliateSdk.showReferAFriend(
context,
options: constReferAFriendOptions(
strings: ReferralStrings(
joinIntro: 'Receba o seu link para partilhar com amigos.',
emailLabel: 'Email',
nameLabel: 'Nome',
joinButton: 'Obter o meu link',
codeSentNotice: 'Enviamos um codigo de 6 digitos para {email}.',
verifyButton: 'Confirmar',
premiumUntil: 'Premium gratuito ate {date}',
errorNetwork: 'Sem ligacao. Tente novamente.',
),
),
);
Key
Default
joinIntro
Get your own link to share with friends.
emailLabel
Email
nameLabel
Name
joinButton
Get my link
codeSentNotice
We emailed a 6-digit code to {email}. Enter it below to connect.
codeLabel
Code
verifyButton
Verify
resendButton
Send a new code
codeResentNotice
We sent a new code. Check your email.
differentEmailButton
Use a different email
codeLabelTitle
Your code
copyButton
Copy
copiedNotice
Code copied
linkCopiedNotice
Link copied
dashboardCopiedNotice
Dashboard link copied
shareButton
Share my link
shareCodeButton
Share my code (Short Code Only apps)
referralsLabel
Referrals
earnedLabel
Earned
premiumUntil
Free premium until {date}
rewardsHeading
Your rewards
redeemButton
Redeem
dashboardLink
Open my dashboard
closeButton
Close (the close button's tooltip)
loading
Loading... (screen reader label on the spinner)
tryAgainButton
Try again
errorProgramDisabled
Referrals are not available in this app right now.
errorAffiliateLimitReached
The referral program is full right now. Please try again later.
errorInvalidCode
That code is wrong or has expired. Check the email or send a new code.
errorTooManyCodes
Too many codes requested. Please wait a while and try again.
errorRateLimited
Too many attempts. Please wait a moment and try again.
errorInvalidEmail
Please enter a valid email address.
errorNetwork
Could not connect. Check your connection and try again.
errorServer
Something went wrong. Please try again.
errorCodeIncomplete
Enter the 6-digit code from the email.
errorShareFailed
Could not open the share sheet. Copy your link instead.
The headline and the reward line are not in strings: they stay on headline and rewardText, which fall back to your dashboard settings.
Headless methods (build your own UI):
// Make the user a referrer. appUserId and playPurchaseToken are optional (see Automatic rewards below).final result = await insertAffiliateSdk.createAffiliateForUser(
'jane@example.com',
'Jane',
appUserId: revenueCatAppUserId,
);
switch (result.status) {
caseAffiliateEnrolmentStatus.created:
print('Share this: ${result.affiliate?.deeplinkUrl}');
break;
caseAffiliateEnrolmentStatus.verificationRequired:
// The email is already an affiliate. We emailed them a 6-digit code.final verified = await insertAffiliateSdk.verifyAffiliateCode('jane@example.com', codeFromUser);
break;
caseAffiliateEnrolmentStatus.error:
print('${result.errorCode}: ${result.errorMessage}');
break;
default:
break;
}
// Referral stats (null when this device is not connected)final me = await insertAffiliateSdk.getMyAffiliateDetails();
if (me != null) {
print('${me.referralCount} referrals, earned ${me.totalEarned}${me.currency}');
print('${me.rewardsGranted} rewards, premium until ${me.premiumUntil}');
for (final reward in me.rewardCodes) {
print('${reward.store}${reward.code}: ${reward.redeemUrl}'); // newest first
}
}
// The user subscribed or logged in after joining: save their account so waiting rewards are grantedawait insertAffiliateSdk.setReferrerAccount(appUserId: revenueCatAppUserId);
final isReferrer = await insertAffiliateSdk.isUserAnAffiliate(); // local check, no networkfinal config = await insertAffiliateSdk.getReferralProgramConfig(); // dashboard settingsawait insertAffiliateSdk.shareReferralLink(message: 'Join me on MyApp: {link}'); // system share sheetawait insertAffiliateSdk.signOutAffiliate(); // call when your user logs out
referralCount is the count for what your dashboard counts as a referral (install, a tracked event, or a purchase). It only goes up, so you can compare it with what you have already rewarded and grant the difference.
How the device stays connected: enrolling stores a private token for your company code in the SDK's app storage (the token is never logged). If the app is deleted, or the user moves to a new phone, calling createAffiliateForUser again with the same email sends them a code to reconnect. Their affiliate account and earnings are untouched.
Rewarding referrers: values on the device are for display. A modified device can fake them, so grant anything of real value (credits, premium time) from your server using the referral.created webhook or the Public API. For free premium time, use Apple/Google offer codes or RevenueCat promotional entitlements.
Automatic rewards: when referrer rewards are set up in the dashboard (RevenueCat, Adapty, App Store offer codes or Google Play), Insert Affiliate grants them for you. To know who to reward, pass the referrer's appUserId (RevenueCat app user id or Adapty customer user id) and/or playPurchaseToken (their own Google Play subscription purchase token) to createAffiliateForUser / verifyAffiliateCode. The drop-in screen takes the same values as appUserId / playPurchaseToken options: it sends them when the user enrols, and calls setReferrerAccount for you when it opens for a user who is already enrolled. If the user subscribes or logs in later, call setReferrerAccount then; any rewards that were waiting are granted. The SDK also sends its device id so a user cannot refer themselves. Reward codes appear in rewardCodes. Each has a store: 'app_store' for an App Store offer code (iOS only) or 'google_play' for a Google Play promo code (Android only, redeemUrl is https://play.google.com/redeem?code=...). Codes from older servers have no store and are read as 'app_store'. The drop-in screen lists only the codes the phone can redeem; rewardCodesForPlatform(codes, platform: defaultTargetPlatform, isWeb: kIsWeb) does the same filtering for your own UI.
Build your own screen: everything the drop-in screen does is a method call, so you can ignore ReferAFriendScreen entirely. In the order you would call them:
getReferralProgramConfig() for the program settings and copy. enabled false means joining is refused.
isUserAnAffiliate() to see whether this device is already connected, without a network call.
Not enrolled:createAffiliateForUser(email, name). status is created (connected, result.affiliate has the code and link), verificationRequired, or error.
Code needed:verifyAffiliateCode(email, code) with the 6-digit code from the email. Call createAffiliateForUser again to send a new code, and go back to step 3 for a different email. Digits typed in any script are accepted.
Enrolled:getMyAffiliateDetails() for the code, link, referralCount, earnings and dashboardUrl.
Rewards: on the same object, rewardsGranted, premiumUntil and rewardCodes (each with code, redeemUrl, store, grantedAt). rewardCodesForPlatform(codes, platform: defaultTargetPlatform, isWeb: kIsWeb) keeps the ones this phone can redeem, and buildReferralShareText(...) builds the same share text the screen uses.
Sharing:shareReferralLink() for the system share sheet, or your own UI around the link and code.
Accounts and sign out:setReferrerAccount(...) when the user subscribes or logs in, signOutAffiliate() when they log out.
Errors:createAffiliateForUser and verifyAffiliateCode return errorCode (PROGRAM_DISABLED, AFFILIATE_LIMIT_REACHED, INVALID_CODE, TOO_MANY_CODES, RATE_LIMITED, INVALID_EMAIL, NETWORK_ERROR, INVALID_COMPANY_ID, INVALID_RESPONSE, or HTTP_<status>) and errorMessage, so you can write your own wording. getMyAffiliateDetails() and getReferralProgramConfig() return null instead of a code: after a null, isUserAnAffiliate() returning true means the device is still connected and the call failed for network reasons, which is exactly how the drop-in screen tells the two apart.
Store rules: the SDK uses the system share sheet only and never reads Contacts. Never gate app features behind sharing, and never reward ratings or reviews.
This feature adds the share_plus dependency for the system share sheet.
🔍 Troubleshooting
Initialization Issues
Error: "Company code is not set"
Cause: SDK not initialized or method called before initialization
Solution: Initialize SDK in main() before runApp()
Deep Linking Issues
Problem: Deep link opens browser instead of app
Cause: Missing or incorrect URL scheme configuration
Solution:
iOS: Add URL scheme to Info.plist and configure associated domains
Android: Add intent filters to AndroidManifest.xml
Problem: "No affiliate identifier found"
Cause: User hasn't clicked an affiliate link yet
Solution: Test with simulator/emulator using adb shell or xcrun simctl openurl
Purchase Tracking Issues
Problem: Purchases not appearing in dashboard
Cause: Webhook not configured or affiliate identifier not passed to IAP platform
Solution:
Verify webhook URL and authorization headers
For RevenueCat: Confirm insert_affiliate attribute is set before purchase
Enable verbose logging and check console for errors