Initializing the SDK
You’ll need your project token to get started!
import 'package:linkrunner/linkrunner.dart';
Future<void> initLinkrunner() async {
try {
// Initialize with your project token
await LinkRunner().init(
'YOUR_PROJECT_TOKEN',
);
print('LinkRunner initialized');
} catch (e) {
print('Error initializing LinkRunner: $e');
}
}
// Call this in your app's initialization
@override
void initState() {
WidgetsFlutterBinding.ensureInitialized(); // Make sure this is added!
super.initState();
initLinkrunner();
}
Note: The initialization method doesn’t return any value. To get attribution data and deeplink information, use the getAttributionData
method.
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": "https://app.yourdomain.com/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"
}
}
Set Additional Data
Use the setAdditionalData
method to set CleverTap ID:
Future<void> setIntegrationData() async {
try {
await LinkRunner().setAdditionalData(
integrationData: {
'clevertap_id': 'YOUR_CLEVERTAP_USER_ID', // CleverTap user identifier
},
);
print('CleverTap ID set successfully');
} catch (e) {
print('Error setting CleverTap ID: $e');
}
}
Parameters for LinkRunner.setAdditionalData
clevertap_id
: 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',
'amount': 99.99, // Include amount as a number for revenue sharing with ad networks like Google and Meta
},
);
print('Event tracked successfully');
} catch (e) {
print('Error tracking event: $e');
}
}
Revenue Sharing with Ad Networks
To enable revenue sharing with ad networks like Google Ads and Meta, include an amount
parameter as a number in your custom event data. This allows the ad networks to optimize campaigns based on the revenue value of conversions:
Future<void> trackPurchaseEvent() async {
try {
await LinkRunner().trackEvent(
eventName: 'purchase_completed',
eventData: {
'product_id': '12345',
'category': 'electronics',
'amount': 149.99, // Revenue amount as a number
},
);
print('Purchase event with revenue tracked successfully');
} catch (e) {
print('Error tracking purchase event: $e');
}
}
For revenue sharing with ad networks to work properly, ensure the amount
parameter is passed as a number (double
or int), not as a string.
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
// type: PaymentType.SECOND_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:
PaymentType.FIRST_PAYMENT
- First payment made by the user
PaymentType.SECOND_PAYMENT
- Second payment made by the user
PaymentType.WALLET_TOPUP
- Adding funds to a wallet
PaymentType.FUNDS_WITHDRAWAL
- Withdrawing funds
PaymentType.SUBSCRIPTION_CREATED
- New subscription created
PaymentType.SUBSCRIPTION_RENEWED
- Subscription renewal
PaymentType.ONE_TIME
- One-time payment
PaymentType.RECURRING
- Recurring payment
PaymentType.DEFAULT_PAYMENT
- Default type (used if not specified)
status
: PaymentStatus (optional) - Status of the payment. Available options:
PaymentStatus.PAYMENT_INITIATED
- Payment has been initiated
PaymentStatus.PAYMENT_COMPLETED
- Payment completed successfully (default if not specified)
PaymentStatus.PAYMENT_FAILED
- Payment attempt failed
PaymentStatus.PAYMENT_CANCELLED
- Payment was cancelled
SDK Signing (Optional)
The SDK signing feature provides the following 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.
import 'dart:io' show Platform;
import 'package:linkrunner/linkrunner.dart';
Future<void> initLinkrunnerWithSigning() async {
try {
// Initialize with your project token and SDK signing parameters
await LinkRunner().init(
'YOUR_PROJECT_TOKEN',
Platform.isIOS ? 'YOUR_IOS_SECRET_KEY' : 'YOUR_ANDROID_SECRET_KEY', // Platform-specific secret key
Platform.isIOS ? 'YOUR_IOS_KEY_ID' : 'YOUR_ANDROID_KEY_ID', // Platform-specific key ID
true, // Optional: Enable debug mode for development (defaults to false)
);
print('LinkRunner initialized with SDK signing');
} catch (e) {
print('Error initializing LinkRunner: $e');
}
}
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().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 {
await LinkRunner().init('YOUR_PROJECT_TOKEN');
setState(() {
_initialized = true;
});
} catch (e) {
print('Error initializing LinkRunner: $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(eventName: 'button_clicked');
},
child: Text('Track Custom Event'),
),
],
),
),
);
}
}
Support
If you encounter issues during integration, contact us at darshil@linkrunner.io.