Flutter plugin for Facebook App Links SDK. This plugin must be used to catch deferred deeplinks sent from Facebook after your app has been installed from a FB ADS.
First of all, if you don't have one already, you must first create an app at Facebook developers: https://developers.facebook.com/
- Android Builds: Java 11 is required for Android compilation
- Environment Variable:
JAVA_HOMEshould point to JDK 11 installation - Android Gradle Plugin: AGP 8.x requires JDK 11 (configured in
android/build.gradle) - Verification: Run
java -versionto confirm JDK 11.x is active
- Xcode: Version 12.0+ for Swift 5.0 compatibility
- iOS Deployment Target: iOS 11.0 minimum (optimized for iOS 12.0+)
- Flutter SDK: 3.2.2+ (Flutter 3.x required for Facebook SDK 18)
- Dart SDK: 3.2.2+ (nullable safety and modern language features)
Facebook advertising tracking is DISABLED by default on iOS. If you do not implement the proper ATT permission flow and call setAdvertiserTrackingEnabled(true), Facebook attribution (including StoreKit2 purchase events) will not work.
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
// In your app's main initialization:
if (Platform.isIOS) {
// 1. Request ATT permission
var status = await AppTrackingTransparency.requestTrackingAuthorization();
// 2. Enable Facebook tracking after consent (REQUIRED for attribution!)
if (status == TrackingStatus.authorized) {
await FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(true); // <-- THIS IS CRITICAL
await FlutterFacebookAppLinks.consentProvided();
}
}
Get your app id (referred to as `[APP_ID]` below)
### Configure Android
For Android configuration, you can follow the same instructions of the Flutter Facebook App Events plugin:
Read through the "[Getting Started with App Events for Android](https://developers.facebook.com/docs/app-events/getting-started-app-events-android)" tutuorial and in particular, follow [step 2](https://developers.facebook.com/docs/app-events/getting-started-app-events-android#2--add-your-facebook-app-id) by adding the following into `/app/res/values/strings.xml` (or into respective `debug` or `release` build flavor)
configure inside android/app/main/res/values/strings.xml the above values (without square brackets):
```xml
<string name="facebook_app_id">[your_app_id]</string>
<!-- Find your client token at: Facebook App Dashboard > Settings > Advanced > Security -->
<string name="facebook_client_token">[your_client_token]</string>then, add that string resource reference to your main AndroidManifest.xml file, within ...
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id" />
<meta-data android:name="com.facebook.sdk.ClientToken" android:value="@string/facebook_client_token"/>If you want to delay event collection (e.g. to obtain GDPR consent), add the following to AndroidManifest.xml inside the <application> tag:
<meta-data android:name="com.facebook.sdk.AutoInitEnabled" android:value="false" />
<meta-data android:name="com.facebook.sdk.AutoLogAppEventsEnabled"
android:value="false"/>Then after consent is obtained, call the following methods in order:
For iOS (with ATT permission):
- Request ATT permission using iOS
ATTrackingManager - Call
FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(true/false)based on user consent - Call
FlutterFacebookAppLinks.consentProvided()orconsentRevoked()
For Android:
- Call
FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(true/false)based on user consent - Call
FlutterFacebookAppLinks.consentProvided()orconsentRevoked()
The setAdvertiserTrackingEnabled() method has platform-specific implementations with different semantics:
- API:
Settings.shared.isAdvertiserTrackingEnabled - Behavior: Controls whether the SDK reports advertiser tracking as enabled to Facebook
- Purpose: Required for proper Facebook attribution after ATT permission
- Timing: Set after obtaining ATT permission from iOS, before
consentProvided()
- API:
FacebookSdk.setAdvertiserIDCollectionEnabled - Behavior: Controls whether the SDK collects the advertising ID
- Purpose: Enables/disables advertising ID collection based on user consent
- Timing: Set before
consentProvided()to respect user privacy settings
Both iOS and Android implementations should be set based on the user's App Tracking Transparency (ATT) consent status on iOS, or equivalent privacy settings on Android. This ensures proper GDPR compliance and Facebook attribution functionality.
Critical Attribution Information:
- iOS 11.0+ minimum supported
- Swift 5.0+ required for basic functionality
- Full Attribution Support: Requires Swift 6.2+ with NonescapableTypes (Xcode 15.0+)
- Fallback Mode: Older Swift versions use limited attribution setup
Attribution Fallback Warning: When Swift 6.2+ NonescapableTypes are not available, the plugin uses a fallback initialization that may not enable full Facebook attribution features. This could affect:
- StoreKit2 purchase event tracking reliability
- Facebook Ads attribution accuracy
- Deferred deep link attribution
Telemetry Logging: The plugin logs when fallback mode is triggered, including iOS version and device information. Monitor console logs for:
FB APP LINKS: ⚠️ ATTRIBUTION FALLBACK TRIGGERED - iOS [version] on [device]
Recommendation:
- Use Xcode 15.0+ for full attribution support
- Test StoreKit2 purchase flows on devices that trigger fallback
- Monitor telemetry logs for fallback usage rates
Your app needs the app_tracking_transparency package to handle ATT permission requests:
dependencies:
flutter_facebook_app_links: ^3.2.0
app_tracking_transparency: ^2.0.4 # Required for iOS ATT compliance
For iOS configuration, you can follow the same instructions of the Flutter Facebook App Events plugin:
Read through the "[Getting Started with App Events for iOS](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios)" tutuorial and in particular, follow [step 4](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios#plist-config) by opening `info.plist` "As Source Code" and add the following
- If your code does not have `CFBundleURLTypes`, add the following just before the final `</dict>` element:
```xml
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb[APP_ID]</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>[APP_ID]</string>
<key>FacebookDisplayName</key>
<string>[APP_NAME]</string>- If your code already contains
CFBundleURLTypes, insert the following:
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb[APP_ID]</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>[APP_ID]</string>
<key>FacebookDisplayName</key>
<string>[APP_NAME]</string>- After obtaining ATT permission, follow the same pattern as Android above: call
FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(true/false)based on user consent, then callFlutterFacebookAppLinks.consentProvided()orconsentRevoked().
This plugin includes comprehensive Facebook Analytics event logging capabilities, allowing you to track user behavior, conversions, and app performance metrics directly in your Flutter app.
Important: Event logging requires explicit user consent for privacy compliance (GDPR, CCPA, ATT). Always obtain user consent before logging events.
import 'dart:io' show Platform;
import 'package:flutter_facebook_app_links/flutter_facebook_app_links.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
// Initialize Facebook SDK with proper consent
Future<void> initializeFacebookSDK() async {
if (Platform.isIOS) {
// Request ATT permission first
var status = await AppTrackingTransparency.requestTrackingAuthorization();
var trackingEnabled = status == TrackingStatus.authorized;
// Set advertiser tracking based on consent
await FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(trackingEnabled);
// Provide consent to enable event logging
await FlutterFacebookAppLinks.consentProvided();
} else {
// Android: Enable tracking and provide consent
await FlutterFacebookAppLinks.setAdvertiserTrackingEnabled(true);
await FlutterFacebookAppLinks.consentProvided();
}
// Now event logging is allowed
}Track any user action or app event:
// Simple event
await FlutterFacebookAppLinks.logEvent('trial_started');
// Event with parameters
await FlutterFacebookAppLinks.logEvent('feature_used', {
'feature_name': 'premium_filter',
'user_type': 'pro',
'session_duration': 300
});Track revenue and purchase conversions:
// Log purchases with currency and amount
await FlutterFacebookAppLinks.logPurchaseEvent(
49.99,
'USD',
{
'fb_content_id': 'product_12345',
'fb_content_type': 'product',
'fb_num_items': 2,
'fb_content_category': 'electronics'
}
);Track user acquisition:
// Log user registrations
await FlutterFacebookAppLinks.logCompleteRegistration('email');
await FlutterFacebookAppLinks.logCompleteRegistration('facebook');Use predefined constants to avoid typos and ensure proper event naming:
// Standard commerce events
await FlutterFacebookAppLinks.logEvent(FacebookEvents.purchase, {
FacebookParameters.contentId: 'product_123',
FacebookParameters.contentType: 'product',
FacebookParameters.currency: 'USD',
'_valueToSum': 29.99, // Standard Facebook parameter
});
await FlutterFacebookAppLinks.logEvent(FacebookEvents.addToCart, {
FacebookParameters.contentId: 'product_456',
FacebookParameters.contentType: 'product',
FacebookParameters.numItems: 1
});
// Standard engagement events
await FlutterFacebookAppLinks.logEvent(FacebookEvents.completeRegistration, {
FacebookParameters.registrationMethod: 'email'
});
await FlutterFacebookAppLinks.logEvent(FacebookEvents.viewContent, {
FacebookParameters.contentId: 'article_789',
FacebookParameters.contentType: 'article'
});The FacebookEvents class provides constants for all major Facebook events:
Commerce Events:
FacebookEvents.purchase- Track revenueFacebookEvents.addToCart- Add to cart actionsFacebookEvents.addToWishlist- Wishlist additionsFacebookEvents.initiateCheckout- Checkout starts
Engagement Events:
FacebookEvents.completeRegistration- User signupsFacebookEvents.viewContent- Content viewsFacebookEvents.search- Search actionsFacebookEvents.contact- Contact form submissions
Achievement Events:
FacebookEvents.completeTutorial- Tutorial completionFacebookEvents.achieveLevel- Level progression
Custom Events:
FacebookEvents.trialStarted- Trial initiationsFacebookEvents.featureUsed- Feature usageFacebookEvents.screenViewed- Screen tracking
The FacebookParameters class provides constants for standard Facebook parameter names to prevent typos:
Content Parameters:
FacebookParameters.contentId- Content identifier (fb_content_id)FacebookParameters.contentType- Content type/category (fb_content_type)FacebookParameters.contentCategory- Content category (fb_content_category)
Commerce Parameters:
FacebookParameters.numItems- Number of items (fb_num_items)FacebookParameters.currency- Currency code (fb_currency)FacebookParameters.value- Monetary value (fb_value)
User Parameters:
FacebookParameters.registrationMethod- Registration method (fb_registration_method)FacebookParameters.searchString- Search query (fb_search_string)FacebookParameters.description- Description (fb_description)
The plugin includes comprehensive validation:
- Event Names: 1-40 characters, alphanumeric + underscores only
- Currencies: Must be valid 3-letter ISO codes (USD, EUR, GBP, etc.)
- Amounts: Must be >= 0 for purchases
- Consent: Events require prior consent via
consentProvided()
Invalid inputs throw ArgumentError or StateError with clear messages.
For complete event specifications and best practices:
Important Behavioral Change: iOS initFBLinks() now uses a hybrid caching strategy for optimal performance and platform consistency:
- Fast Path: Returns cached deep link immediately if available (populated during app launch)
- Fallback Path: Performs network fetch if no cached link exists (~2-5 seconds blocking)
- Platform Consistency: Now matches Android behavior while avoiding UI freezes
Performance Characteristics:
- During App Launch: Fast cached response (recommended usage)
- Before App Launch Completes: May perform network fetch (potentially blocking)
- After Cache Population: Always immediate response
Migration Notes:
- Before: Method returned empty strings (cached-only limitation)
- After: Actually returns deep link results via hybrid approach
- Error Handling: Now properly throws exceptions (previously silent failures)
Important: initFBLinks() behaves differently on iOS vs Android due to platform-specific caching strategies:
iOS (Cached Strategy - Fast but Timing-Dependent):
- Returns cached deep link value immediately
- Advantage: No network delay, never blocks UI
- Limitation: Returns empty string if called before
didFinishLaunchingWithOptionscompletes - Best Practice: Call during app initialization after launch completes
Android (Network Strategy - Slower but Always Fresh):
- Performs network fetch to get latest deep link data
- Advantage: Always attempts to retrieve current deep link
- Limitation: May have network latency, potential for timeouts
- Behavior: Consistent regardless of call timing
Cross-Platform Consistency:
For consistent behavior across platforms, use getDeepLinkUrl() which performs on-demand network fetching on both iOS and Android.
Example:
// iOS: May return empty if called too early in app launch
// Android: Always fetches fresh data
String deepLink = await FlutterFacebookAppLinks.initFBLinks();
// For guaranteed fresh data on both platforms:
// iOS & Android: Always performs network fetch
String freshLink = await FlutterFacebookAppLinks.getDeepLinkUrl();import 'dart:io' show Platform;
/// FB Deferred Deeplinks - Updated for iOS hybrid caching
void initFBDeferredDeeplinks() async {
String deepLinkUrl;
try {
// iOS: Hybrid caching - fast cached response or network fallback
// Android: Consistent network fetch behavior
deepLinkUrl = await FlutterFacebookAppLinks.initFBLinks();
// Note: iOS no longer requires separate getDeepLink() call
// The method now actually returns deep link results
if (deepLinkUrl.isNotEmpty) {
// Handle successful deep link
navigateToDeepLink(deepLinkUrl);
} else {
// No deep link available (normal case)
proceedWithNormalFlow();
}
} catch (e) {
// Network errors or SDK issues now properly propagated
// (Previously: Silent failures with empty strings)
handleDeepLinkError(e);
}
}Best Practices:
- Call
initFBLinks()during app initialization for optimal performance - Handle exceptions appropriately (network issues, SDK errors)
- Test on slow networks to ensure acceptable user experience
Please refer to the official SDK documentation for Android and iOS.
How documented on Facebook docs, starting from v5.0.0 of the SDK, they introduce a flag for disabling automatic SDK initialization to be GDPR compliant.
It means that you should collect user consent before you use call the method initFBLinks() of this plugin and save the user choice. Moreover, you should give the user a chance to revoke their consent in the future.
Please keep in mind that this plugin uses FacebookSDK.setAutoInitEnabled(true) in Android and Settings.isAutoInitEnabled = true in iOS by default, so the consent must be granted in your Dart code before you call FlutterFacebookAppLinks.initFBLinks().
To correctly test deferred deeplinks, DO NOT use the preview of your FB ADS campaign. Instead, use this tool APP ADS HELPER
At the end of the page you will find a "Test deep link" button, click on it and type your custom url scheme (deeplink), for example: myawesomeapp://screen/login
Select the second checkbox (or both). Remember that to make it works, you'll need the Facebook app installed on your device (Android or iPhone) and you must be logged in with the same account you're using in the Facebook Developers console.
Your app doesn't need to be published on the store, simply uninstall it and re-install using Android Studio/VSCode or XCode after you've sent the deferred deep link.