Native Flutter SDK for deep linking, mobile attribution, and conversion tracking.
- Deferred Deep Linking: Match app installs to link clicks using privacy-compliant fingerprinting
- Universal Links & App Links: Full support for iOS Universal Links and Android App Links (HTTPS deep links)
- Custom URL Schemes: Handle custom app URL schemes
- Event Tracking: Track in-app events and conversions
- Offline Support: Queue events when offline with automatic retry
- Privacy-First: No IDFA/GAID collection by default, complies with privacy requirements
- Programmatic Link Creation: Create short links directly from your app
- Pure Dart/Flutter: 100% Dart, standard
Future/StreamAPIs
- Flutter 3.10+
- Dart 3.0+
- iOS 12.0+
- Android API 21+
Add the following to your pubspec.yaml file:
dependencies:
forty_link: ^0.0.1Or run:
flutter pub add forty_linkInitialize the SDK in your main.dart or early in your app lifecycle.
import 'package:forty_link/link_forty.dart';
import 'package:forty_link/models/link_forty_config.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize LinkForty
try {
final config = LinkFortyConfig(
baseURL: Uri.parse('https://go.yourdomain.com'),
apiKey: 'your-api-key', // Optional for self-hosted
debug: true, // Enable debug logging
attributionWindowHours: 168, // 7 days
);
final response = await LinkForty.initialize(config: config);
print('LinkForty initialized. Install ID: ${response.installId}');
} catch (e) {
print('LinkForty initialization failed: $e');
}
runApp(const MyApp());
}Register a callback to handle deferred deep links. This is triggered when the app is installed from a link.
// checking attribution status
LinkForty.instance.onDeferredDeepLink((deepLinkData) {
if (deepLinkData != null) {
// User installed from a link - navigate to content
print("Install attributed to: ${deepLinkData.shortCode}");
print("UTM Source: ${deepLinkData.utmParameters?.source ?? "none"}");
// Navigate to the right content
final productId = deepLinkData.customParameters?['productId'];
if (productId != null) {
navigatorKey.currentState?.pushNamed('/product', arguments: productId);
}
} else {
// Organic install - no attribution
print("Organic install");
}
});The SDK uses app_links internally to handle incoming links. You just need to register a callback.
// Handle deep links when app is running or opened from background
LinkForty.instance.onDeepLink((uri, deepLinkData) {
print("Deep link opened: $uri");
if (deepLinkData != null) {
print("Link data: $deepLinkData");
// Navigate using deep link path (e.g., /products/123)
if (deepLinkData.deepLinkPath != null) {
navigatorKey.currentState?.pushNamed(deepLinkData.deepLinkPath!);
}
}
});Server-side resolution: When the SDK is initialized, deep links are automatically resolved via the server to provide enriched data including
deepLinkPath,appScheme, andlinkId. If the server is unreachable, the SDK falls back to local URL parsing.
// Track a simple event
await LinkForty.instance.trackEvent('button_clicked');
// Track event with properties
await LinkForty.instance.trackEvent(
'purchase',
{
'product_id': '123',
'amount': 29.99,
'currency': 'USD',
},
);
// Track revenue
await LinkForty.instance.trackRevenue(
amount: 29.99,
currency: 'USD',
properties: {'product_id': '123'},
);import 'package:forty_link/models/create_link_options.dart';
import 'package:forty_link/models/utm_parameters.dart';
// ...
final result = await LinkForty.instance.createLink(
CreateLinkOptions(
deepLinkParameters: {'route': 'VIDEO_VIEWER', 'id': 'vid123'},
title: 'Check this out!',
utmParameters: UTMParameters(source: 'app', campaign: 'share'),
),
);
print("Share this link: ${result.url}");
// e.g., "https://go.yourdomain.com/tmpl/abc123"Note: Requires an API key in
LinkFortyConfig.
- AndroidManifest.xml: Add the intent filters for App Links (HTTPS) and Custom Schemes.
<activity ...>
<!-- App Links (Standard HTTPS) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="go.yourdomain.com" />
</intent-filter>
<!-- Custom Scheme (Optional) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourapp" />
</intent-filter>
</activity>- Asset Links: Ensure your
.well-known/assetlinks.jsonis hosted on your domain.
- Universal Links: Add the Associated Domains capability in Xcode.
applinks:go.yourdomain.com
- Custom URL Schemes: Add your scheme in
Info.plistunder URL Types. - AASA File: Ensure your
.well-known/apple-app-site-associationis hosted on your domain.
If you're running your own LinkForty Core instance:
final config = LinkFortyConfig(
baseURL: Uri.parse('https://links.yourcompany.com'),
apiKey: null, // No API key needed for self-hosted
debug: false,
);
await LinkForty.initialize(config: config);final installData = LinkForty.instance.getInstallData();
if (installData != null) {
print("Short code: ${installData.shortCode}");
print("UTM source: ${installData.utmParameters?.source ?? "none"}");
}
final installId = LinkForty.instance.getInstallId();
if (installId != null) {
print("Install ID: $installId");
}// Check queued events count
final count = LinkForty.instance.queuedEventCount;
// Manually flush event queue
await LinkForty.instance.flushEvents();
// Clear event queue
LinkForty.instance.clearEventQueue();await LinkForty.instance.clearData();
// Reset SDK to uninitialized state (only for testing)
LinkForty.instance.reset();- No Persistent IDs: Uses probabilistic fingerprinting only.
- Data Minimization: Collects only necessary attribution data.
- User Control: Provides
clearData()for user data deletion.
- Device timezone
- Device language
- Screen resolution
- OS version (Android/iOS)
- App version
- User-Agent string
The SDK enforces HTTPS for all API endpoints (except localhost, 127.0.0.1, and 10.0.2.2 for development).
- Documentation: https://docs.linkforty.com
- Issues: GitHub Issues
LinkForty Flutter SDK is available under the MIT license. See LICENSE for more info.