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.
import 'package:linkrunner/linkrunner.dart';
Future<void> initLinkrunner() async {
try {
// Initialize with your project token
final initData = await Linkrunner.init('YOUR_PROJECT_TOKEN');
print('Linkrunner initialized: $initData');
} 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 {
final result = 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
),
additionalData: {}, // Optional: Any additional data
);
print('Signup successful: $result');
} catch (e) {
print('Error during signup: $e');
}
}
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');
}
}
Handling Deferred Deep Links
triggerDeeplink
is an optional function which has to be called once after the user onboards and the main stack is ready for navigation. This function handles deferred deep links (links that led to app installation):
Future<void> triggerDeeplink() {
try {
Linkrunner.triggerDeeplink();
} catch (e) {
print('Error triggering deeplink: $e');
}
}
// Call this after your navigation is ready
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_isNavigationReady) {
triggerDeeplink();
}
}
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, // Payment amount
userId: 'user123', // User identifier
paymentId: 'payment456', // Optional: Unique payment identifier
type: PaymentType.FIRST_PAYMENT, // optional
status: PaymentStatus.PAYMENT_COMPLETED, // optional
),
);
print('Payment captured successfully');
} catch (e) {
print('Error capturing payment: $e');
}
}
Parameters for Linkrunner.capturePayment
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
- 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
Removing Payments
Remove payment records (for refunds or cancellations):
Future<void> removePayment() async {
try {
await Linkrunner.removePayment(
userId: 'user123', // User identifier
paymentId: 'payment456', // Optional: Unique payment identifier
);
print('Payment removed successfully');
} catch (e) {
print('Error removing payment: $e');
}
}
Parameters for Linkrunner.removePayment
userId
: String (required) - Identifier for the user whose payment is being removed
paymentId
: String (optional) - Unique identifier for the payment to be removed
Note: Either paymentId
or userId
must be provided when calling removePayment
. If only userId
is provided, all payments for that user will be removed.
Function Placement Guide
Function | Where to Place | When to Call |
---|
Linkrunner.init | Main app initState or equivalent | Once when app starts |
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.