Using the Flutter SDK
This guide will help you implement Linkrunner functionality in your Flutter application.
Initializing the SDK
Initialize the Linkrunner SDK when your app starts:
You can find your project token here.
SDK Signing Parameters (Optional)
For enhanced security, the LinkRunner SDK requires the following signing parameters during initialization:
secretKey
: A unique secret key used for request signing and authentication
keyId
: A unique identifier for the key pair used in the signing process
You can find your project token, secret key, and key ID here.
Note: The initialization method doesn’t return any value. To get attribution data and deeplink information, use the getAttributionData
method.
import 'package:linkrunner/linkrunner.dart';
Future<void> initLinkrunner() async {
try {
// Initialize with your project token and SDK signing parameters
await Linkrunner.init(
'YOUR_PROJECT_TOKEN',
'YOUR_SECRET_KEY', // Optional: Required for SDK signing
'YOUR_KEY_ID', // Optional: Required for SDK signing
);
print('Linkrunner initialized');
} catch (e) {
print('Error initializing Linkrunner: $e');
}
}
// Call this in your app's initialization
@override
void initState() {
super.initState();
initLinkrunner();
}
User Registration
Call the signup
method once after the user has completed your app’s onboarding process:
It is strongly recommended to use the integrated platform’s identify function to set a persistent user_id once it becomes available (typically after signup or login).
If the platform’s identifier function is not called, you must provide a user identifier for Mixpanel, PostHog, and Amplitude integration.
- mixpanelDistinctId for Mixpanel
- amplitudeDeviceId for Amplitude
- posthogDistinctId for PostHog
Future<void> onSignup() async {
try {
await Linkrunner.signup(
userData: LRUserData(
id: '123', // Required: User ID
name: 'John Doe', // Optional
phone: '9876543210', // Optional
email: 'user@example.com', // Optional
// These properties are used to track reinstalls
userCreatedAt: '2024-01-01T00:00:00Z', // Optional
isFirstTimeUser: true, // Optional
mixpanelDistinctId: 'mixpanelDistinctId', // Optional - Mixpanel Distinct ID
amplitudeDeviceId: 'amplitudeDeviceId', // Optional - Amplitude User ID
posthogDistinctId: 'posthogDistinctId', // Optional - PostHog Distinct ID
),
data: {}, // Optional: Any additional data
);
print('Signup successful');
} catch (e) {
print('Error during signup: $e');
}
}
Getting Attribution Data
To get attribution data and deeplink information for the current installation, use the getAttributionData
function:
Future<void> getAttributionInfo() async {
try {
final attributionData = await Linkrunner.getAttributionData();
print('Attribution data: $attributionData');
} catch (e) {
print('Error getting attribution data: $e');
}
}
The getAttributionData
function returns an AttributionData
object with the following structure:
class AttributionData {
final String? deeplink; // Optional: The deep link URL that led to app installation
final CampaignData campaignData; // Required: Campaign information
}
class CampaignData {
final String id; // Required: Campaign ID
final String name; // Required: Campaign name
final String? adNetwork; // Optional: "META" | "GOOGLE" | null
final String? groupName; // Optional: Campaign group name
final String? assetGroupName; // Optional: Asset group name
final String? assetName; // Optional: Asset name
final String type; // Required: Campaign type ("ORGANIC" | "INORGANIC")
final String installedAt; // Required: Installation timestamp
final String storeClickAt; // Required: Store click timestamp
}
Example response:
{
"deeplink": "myapp://product/123",
"campaign_data": {
"id": "camp_123",
"name": "Summer Sale 2024",
"ad_network": "META",
"group_name": "iOS Campaign",
"asset_group_name": "Product Catalog",
"asset_name": "Banner Ad 1",
"type": "INORGANIC",
"installed_at": "2024-03-20T10:30:00Z",
"store_click_at": "2024-03-20T10:29:45Z"
}
}
Setting User Data
Call setUserData
each time the app opens and the user is logged in:
Future<void> setUserData() async {
try {
await Linkrunner.setUserData(
userData: LRUserData(
id: '123', // Required: User ID
name: 'John Doe', // Optional
phone: '9876543210', // Optional
email: 'user@example.com', // Optional
mixpanelDistinctId: 'mixpanelDistinctId', // Optional - Mixpanel Distinct ID
amplitudeDeviceId: 'amplitudeDeviceId', // Optional - Amplitude User ID
posthogDistinctId: 'posthogDistinctId', // Optional - PostHog Distinct ID
),
);
print('User data set successfully');
} catch (e) {
print('Error setting user data: $e');
}
}
Set Additional Data
Use the setAdditionalData
method to set CleverTap ID:
Future<void> setIntegrationData() async {
try {
await Linkrunner.setAdditionalData(
integrationData: {
'clevertapId': 'YOUR_CLEVERTAP_USER_ID', // CleverTap user identifier
},
);
print('CleverTap ID set successfully');
} catch (e) {
print('Error setting CleverTap ID: $e');
}
}
Parameters for Linkrunner.setAdditionalData
clevertapId
: String (optional) - CleverTap user identifier
This method allows you to connect user identities across different analytics and marketing platforms.
Tracking Custom Events
Track custom events in your app:
Future<void> trackEvent() async {
try {
await Linkrunner.trackEvent(
eventName: 'purchase_initiated', // Event name
eventData: { // Optional: Event data
'product_id': '12345',
'category': 'electronics',
},
);
print('Event tracked successfully');
} catch (e) {
print('Error tracking event: $e');
}
}
Revenue Tracking
Capturing Payments
Track payment information:
Future<void> capturePayment() async {
try {
await Linkrunner.capturePayment(
capturePayment: LRCapturePayment(
amount: 99.99, // Required: Payment amount
userId: 'user123', // Required: User identifier
paymentId: 'payment456', // Optional: Unique payment identifier
type: PaymentType.FIRST_PAYMENT, // Optional: Payment type
status: PaymentStatus.PAYMENT_COMPLETED, // Optional: Payment status
),
);
print('Payment captured successfully');
} catch (e) {
print('Error capturing payment: $e');
}
}
Future<void> removePayment() async {
try {
await Linkrunner.removePayment(
removePayment: LRRemovePayment(
userId: 'user123', // Either userId or paymentId must be provided
paymentId: 'payment456', // Optional: Unique payment identifier
),
);
print('Payment removed successfully');
} catch (e) {
print('Error removing payment: $e');
}
}
Parameters for LRCapturePayment
amount
: double (required) - The payment amount
userId
: String (required) - Identifier for the user making the payment
paymentId
: String (optional) - Unique identifier for the payment
type
: PaymentType (optional) - Type of payment. Available options:
FIRST_PAYMENT
- First payment made by the user
WALLET_TOPUP
- Adding funds to a wallet
FUNDS_WITHDRAWAL
- Withdrawing funds
SUBSCRIPTION_CREATED
- New subscription created
SUBSCRIPTION_RENEWED
- Subscription renewal
ONE_TIME
- One-time payment
RECURRING
- Recurring payment
DEFAULT_PAYMENT
- Default type (used if not specified)
status
: PaymentStatus (optional) - Status of the payment. Available options:
PAYMENT_INITIATED
- Payment has been initiated
PAYMENT_COMPLETED
- Payment completed successfully (default if not specified)
PAYMENT_FAILED
- Payment attempt failed
PAYMENT_CANCELLED
- Payment was cancelled
Enhanced Privacy Controls
The SDK offers options to enhance user privacy:
// Enable PII (Personally Identifiable Information) hashing
Linkrunner.enablePIIHashing(true);
When PII hashing is enabled, sensitive user data like name, email, and phone number are hashed using SHA-256 before being sent to Linkrunner servers.
Function Placement Guide
Function | Where to Place | When to Call |
---|
Linkrunner.init | App initialization | Once when app starts |
Linkrunner.getAttributionData | Attribution data handling flow | Whenever the attribution data is needed |
Linkrunner.setAdditionalData | Integration code | When third-party integration IDs are available |
Linkrunner.signup | Onboarding flow | Once after user completes onboarding |
Linkrunner.setUserData | Authentication logic | Every time app opens with logged-in user |
Linkrunner.triggerDeeplink | After navigation init | Once after navigation is ready |
Linkrunner.trackEvent | Throughout app | When specific user actions occur |
Linkrunner.capturePayment | Payment processing | When user makes a payment |
Linkrunner.removePayment | Refund flow | When payment needs to be removed |
Complete Example
Here’s a simplified example showing how to integrate Linkrunner in a Flutter app:
You can find your project token here.
import 'package:flutter/material.dart';
import 'package:linkrunner/linkrunner.dart';
final linkrunner = LinkRunner();
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Linkrunner Demo',
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool _initialized = false;
@override
void initState() {
super.initState();
_initializeLinkrunner();
}
Future<void> _initializeLinkrunner() async {
try {
final initData = await Linkrunner.init('YOUR_PROJECT_TOKEN');
setState(() {
_initialized = true;
});
// Handle any deeplinks after navigation is set up
WidgetsBinding.instance.addPostFrameCallback((_) {
triggerDeeplink();
});
} catch (e) {
print('Error initializing Linkrunner: $e');
}
}
Future<void> triggerDeeplink() async {
try {
await Linkrunner.triggerDeeplink();
} catch (e) {
print('Error triggering deeplink: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Linkrunner Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Linkrunner ${_initialized ? 'Initialized' : 'Initializing...'}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await Linkrunner.trackEvent('button_clicked');
},
child: Text('Track Custom Event'),
),
],
),
),
);
}
}
Support
If you encounter issues during integration, contact us at darshil@linkrunner.io.