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');
  }
}

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

FunctionWhere to PlaceWhen to Call
Linkrunner.initMain app initState or equivalentOnce when app starts
Linkrunner.signupOnboarding flowOnce after user completes onboarding
Linkrunner.setUserDataAuthentication logicEvery time app opens with logged-in user
Linkrunner.triggerDeeplinkAfter navigation initOnce after navigation is ready
Linkrunner.trackEventThroughout appWhen specific user actions occur
Linkrunner.capturePaymentPayment processingWhen user makes a payment
Linkrunner.removePaymentRefund flowWhen 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.