Singular SDK Integration for Flutter

The Singular SDK is available as a plug-in for Flutter. The instructions below show you how to integrate Singular into your Flutter app.

Download Singular Flutter SDK version 1.2.0
Guide for Engineering Teams
Prerequisites
  • This article assumes you have a functional Flutter app.
  • To integrate the Singular SDK, you need to acquire your SDK Key and Secret. You can find them by logging into your Singular account and going to Developer Tools > SDK Keys.

Integrating the Singular Plugin

To add the Singular plugin to your Flutter app, add the following lines to your pubspec.yaml file:

dependencies:
  singular_flutter_sdk: ^1.2.0

Then navigate to your project in the terminal and run the following:

flutter packages get

Additional Steps for Android

Adding Dependencies

For Android apps, you need to add the Singular library to the dependencies list in app/build.gradle, as follows:

dependencies {
  implementation 'com.google.android.gms:play-services:6.5.87'
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'com.android.support:appcompat-v7:21.0.3'
  ...
}

Note: If you are presented with a DuplicateClasses error at build time, you may already have Google play-services, and you can comment out the dependency.

If you have disabled transitive dependencies for the Singular SDK, add the following to your app's build.gradle.

implementation 'com.android.installreferrer:installreferrer:2.2'
implementation 'com.google.android.gms:play-services-appset:16.0.0'

If your app doesn't implement Google Play Services API 17.0.0 or higher, add the following dependency to your app's build.gradle:

implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0+'

Adding Permissions

Add these permissions under the <manifest> tag in your AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="BIND_GET_INSTALL_REFERRER_SERVICE" />
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />

If your app build is targeting Android 12/API level 31 or higher, add permissions to access the Google Advertising ID:

<uses-permission android:name="com.google.android.gms.permission.AD_ID" />

Note: Do not add this permission if you're integrating the Kids SDK.

Caution: If your app has the android.permission.GET_TASKS permission, the app may be initialized before the user actually opens it. This may initialize the Singular SDK and cause discrepancies in install time. To prevent the problem, remove the permission if it's not needed, or move the Singular SDK initialization call somewhere else in the code, ensuring it is called only after the user opens the app for the first time.

Additional Steps for iOS

To use the Singular plugin, you have to add the AdServices framework.

Initializing the Singular SDK

The Singular SDK initialization code should be called every time your app is opened. It is a prerequisite to all Singular attribution functionality, and it also sends a new user session to Singular (sessions are used to calculate user retention).

The initialization code goes in your main app widget (ie. main.dart) - the first one that loads when the app is opened. This widget has to be stateful, and the code has to be added in the widget's initState() method.

First, you have to create a SingularConfig object. The object contains your Singular SDK Key and Secret (which you can retrieve by logging into your Singular account and going to Developer Tools > SDK Keys).

Optionally, you can add settings to enable various SDK features. See the full list of options.

Example:

import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
  
  ...
  
  class MyHomePage extends StatefulWidget { 
      ...
  }
  
  class _MyHomePageState extends State<MyHomePage> {
      ...
      @override 
      void initState() {
              super.initState();
... SingularConfig config = new SingularConfig('SDK KEY', 'SDK SECRET');
// Set hashed User ID if available
config.customUserId = "b642b4217b34b1e8d3bd915fc65c4452";

// For iOS (Remove this if you are not displaying an ATT prompt)!
config.waitForTrackingAuthorizationWithTimeoutInterval = 300;

// To enable SkAdNetwork Support
config.skAdNetworkEnabled = true;

// (optional) Using Singular Global Properties feature to capture
// third party identifiers. The respective SDK(s) must be initialized
// before the Singular SDK. Example of passing the CleverTapID.
// var cleverTapId = CleverTapPlugin.getCleverTapID();
// config.withGlobalProperty("CLEVERTAPID", cleverTapId, true);
Singular.start(config); }

Tracking Events

Singular can collect data about in-app events to help analyze the performance of your campaigns and measure KPIs. For example, your organization may want to collect data about user logins, registrations, tutorial completions, or leveling up in a gaming app.

Singular supports a variety of standard events. These commonly used events are often supported by ad networks for reporting and optimization. Another advantage is that when you use standard event names, Singular recognizes them automatically and adds them to the Events list without you having to define them manually. We recommend using standard events whenever possible.

The list of events sent to Singular (with the accompanying attributes) should be compiled by the UA/marketing/business team based on your organization's marketing KPIs. The business team can follow the guide at How to Track In-App Events: Guide For Singular Attribution Customers.

With each event you track, you can pass various attributes. See the recommended standard attributes per event.

In your code, send events to Singular using the event or eventWithArgs methods.

Note: For standard events, use the event's Flutter name as it appears in the Flutter SDK List of Standard Events and Attributes, e.g., sngLogin.

For custom events, events that your organization wants to measure that do not match any of Singular's standard events, use any custom name (maximum of 32 characters). We recommend using names in English for compatibility with any ad network partners that may receive the event from Singular for optimization purposes.

Example:

Singular.event(Events.sngLogin);
Singular.eventWithArgs(eventName, {attributeName:attributeValue});
Map<String, Object> map = HashMap<String, Object>();
map ['name'] = 'John Doe';
map ['age'] = 30;
map ['isStudent'] = false;
Singular.eventWithArgs('event_Name', map);

Tracking Revenue

Sending IAP Events

To let Singular track how much revenue your app is making, send IAP events to Singular. By sending the IAP event you also allow Singular to check the event verification data and make sure it's not fraudulent.

See the following example.

Note: This code snippet requires the Flutter IAP package at https://pub.dev/packages/in_app_purchase.

import 'package:singular_flutter_sdk/singular_iap.dart';
import 'dart:io' show Platform;
            
if (Platform.isIOS) {
  singularPurchase = new SingularIOSIAP(
    product.rawPrice.toStringAsFixed(2),
    product.currencyCode,
    purchase.productID,
    purchase.purchaseID,
    purchase.verificationData.serverVerificationData
  );
}
            
else if (Platform.isAndroid) {
  singularPurchase = new SingularAndroidIAP(
    product.rawPrice.toStringAsFixed(2),
    product.currencyCode,
    purchase.verificationData.serverVerificationData,
    purchase.verificationData.localVerificationData
  );
}
            
Singular.inAppPurchase(eventName, singularPurchase);

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

Alternative Method: Sending Custom Revenue Events

Singular also offers the option of reporting revenue by just sending a custom revenue event with a name and a revenue amount. Note that this method does not share the purchase receipt with Singular and therefore, does not allow Singular to verify that it's a legitimate event.

For example:

Singular.customRevenue("MyCustomRevenue", "USD", 5.50);
Map<String, Object> map = HashMap<String, Object>();
map ['name'] = 'John Doe';
map ['age'] = 30;
map ['isStudent'] =false;
Singular.customRevenueWithAttributes('MyCustomRevenue','USD', 20, map);

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

Adding Ad Revenue Attribution Support

You can set up ad revenue attribution through the Singular SDK.

To add ad revenue attribution support using the Flutter SDK:

  1. If you haven't done so yet, contact your Singular Customer Success Manager to enable ad revenue attribution for your account.
  2. Add the appropriate code snippet to get ad revenue information from the mediation platform you use for ad revenue data.

    See code snippets in our Android SDK documentation.

  3. Report the ad revenue to Singular using the following code:

    SingularAdData adData = SingularAdData("Medation_Platform","USD",0.05)
    Singular.adRevenue(adData)

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

Implementing Deep Links

Deep links are links that open the app on the user's phone and send the user directly to a specific page or user experience, instead of just the app's main widget. Deep links are usually used in retargeting campaigns, aimed at users who already have the app on their phone but may not have engaged with it for a while. Singular supports deep linking through Singular Links.

Enabling Singular Links

To enable Singular Links in iOS and in Android, see Singular Links Prerequisites.

For Android support add the following code in the project's MainActivity.java file: 

Java Kotlin
import com.singular.flutter_sdk.SingularBridge;
import android.content.Intent;

@Override protected void onNewIntent(@NonNull Intent intent) {   super.onNewIntent(intent);   SingularBridge.onNewIntent(intent); }

For iOS Support, in the project’s AppDelegate.m, add the following:

 

Objective-C Swift
// Top of AppDelegate.m
            
  #import "SingularAppDelegate.h"
  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [GeneratedPluginRegistrant registerWithRegistry:self];
    [SingularAppDelegate shared].launchOptions = launchOptions;
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
  - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
    [[SingularAppDelegate shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
    return [super application:application continueUserActivity:userActivity  restorationHandler:restorationHandler ];
    }
  - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
      [[SingularAppDelegate shared] handleOpenUrl:url options:options];
      return [super application:app openURL:url options: options];
    }

Handling Singular Links

Use Singular's handler mechanism to read the details of the tracking link that led to the app being opened.

For example:

SingularConfig config = new SingularConfig('<SDK KEY>', '<SDK SECRET>');

config.singularLinksHandler = (SingularLinkParams params) { String deeplink = params.deeplink; String passthrough = params.passthrough; bool isDeferred = params.isDeferred; // Add your code here to handle the deep link }); Singular.init(config);

Adding SKAdNetwork Support

To enable SKAdNetwork tracking for your app, enable the skAdNetworkEnabled configuration option before initializing Singular.

Managed Mode (Recommended)

In managed mode, Singular manages the SKAdNetwork conversion value for you automatically, based on a conversion model of your choice that you can set up in the Singular platform. 

To learn more, see Understanding Singular's Conversion Value Management and the SKAdNetwork Model Configuration FAQ. For a step-by-step guide to using SKAdNetwork with Singular, see How to Get Started with SKAdNetwork.

Note: The SKAN Managed mode is already enabled in the Initialization code snippet above. Make sure that you have these configuration items set.

To enable SKAdNetwork in managed mode, use the following code:

SingularConfig config = new SingularConfig('<SDK KEY>', '<SDK SECRET>');
config.skAdNetworkEnabled = true;
config.waitForTrackingAuthorizationWithTimeoutInterval = 300; Singular.init(config);

Manual Mode

If you already have your own strategy and tools for managing the SKAdNetwork conversion value, you can enable SKAdNetwork in manual mode.

SingularConfig config = new SingularConfig('SDK KEY', 'SDK SECRET');
config.skAdNetworkEnabled = true;
config.manualSkanConversionManagement = true;
config.waitForTrackingAuthorizationWithTimeoutInterval = 300; Singular.init(config);

Then, to update the conversion value, use the following code:

ingular.skanUpdateConversionValue(conversionValue)

To track when the conversion value changes, use the following callback function:

config.conversionValueUpdatedCallback = (int conversionValue) {
  print('Received conversionValueUpdatedCallback: ' + conversionValue.toString());
};

To retrieve the current conversion value, use the following code:

Singular.skanGetConversionValue().then((conversionValue) {
  print('conversion value: ' + conversionValue.toString());
});

Tracking Uninstalls

To let Singular track app uninstalls, give Singular the APNS/FCM token, as in the following example:

// iOS
Singular.registerDeviceTokenForUninstall(apnsToken);
            
// Android
Singular.registerDeviceTokenForUninstall(fcmToken);