Starting with flutter_insider 5.5.0, the identity, in-app message, and App Cards tracking methods that used to be fire-and-forget return a Future. You can await a call before making the next one, so your calls run in order.
Two new identity methods, loginAsync and logoutResettingInsiderIDAsync, also resolve to the merged Insider ID. The native SDK and the data it sends do not change, and existing call sites keep compiling.
login and logoutResettingInsiderID keep their void signature and the optional insiderIDResult callback. The awaitable forms are the separate methods loginAsync and logoutResettingInsiderIDAsync.
This guide explains the following concepts:
Prerequisites
Insider Flutter Plugin flutter_insider: 5.5.0 or later
The SDK initialized with FlutterInsider.Instance.init(...) before you call any of the methods below
Identity methods live on the user object, App Cards tracking methods on the App Cards client, and the in-app message and deep link methods on the main SDK singleton.
import 'package:flutter_insider/flutter_insider.dart';
import 'package:flutter_insider/src/identifiers.dart';
final user = FlutterInsider.Instance.getCurrentUser();
final appCards = FlutterInsider.Instance.appCards;FlutterInsiderIdentifiers is not re-exported by the main library. Import package:flutter_insider/src/identifiers.dart where you build identifiers. This is a temporary workaround until the class is exported from the main library.
getCurrentUser() returns null until init has been called. Guard it with ?. or check for null before you call an identity method.
Async model
Two kinds of methods became awaitable.
Kind | Return type | Completes when | Result |
|---|---|---|---|
Tracking and lifecycle | Future<void> | The native SDK call has returned | No value |
Identity | Future<String?> | The native SDK has merged or reset the user, or timeout elapses | The Insider ID, or null |
// Order two calls: the second starts after the first has reached the native SDK
await FlutterInsider.Instance.disableInAppMessages();
await FlutterInsider.Instance.enableInAppMessages();
// Wait for the merged Insider ID
final insiderID = await user.loginAsync(identifiers);Timeout
loginAsync and logoutResettingInsiderIDAsync take a named timeout (default 10 seconds). The native SDK answers only after a successful merge or reset. If the device is offline, a request fails, or a retry is pending, the native SDK never calls back. The Future then resolves to null once timeout elapses.
A timeout does not cancel the native call. The merge can still complete afterward, and the SDK keeps the resulting Insider ID.
timeout must be positive. Raise it per call when you expect a slow network.
After a null, FlutterInsider.Instance.getInsiderID() returns the ID the SDK holds at that moment, which can still be the pre-merge ID. To be told when the merged ID arrives, register FlutterInsider.Instance.registerInsiderIDListener(...).
final insiderID = await user.loginAsync(
identifiers,
timeout: const Duration(seconds: 30),
);Error handling
None of the methods on this page throw. App Cards retrieval methods such as getCampaigns still throw InsiderAppCardsException. See Configure app cards for details.
When a native call fails, the SDK reports the failure and the Future still completes. Future<void> methods complete normally, and the identity methods resolve to null. The SDK also reports a failure that arrives after the timeout.
Three cases resolve loginAsync and logoutResettingInsiderIDAsync to null. You cannot tell them apart from the return value:
Case | What happened |
|---|---|
No ID delivered | The native SDK finished without an Insider ID. For loginAsync, this also happens when the identifier set is empty, and the call is not sent. |
Native failure | The native call failed, and the SDK reported the failure. |
Timeout | timeout elapsed before the native SDK answered. The call can still complete. |
final insiderID = await user.loginAsync(identifiers);
if (insiderID == null) {
// No ID, native failure, or timeout. The SDK can still merge the user later;
// read the current ID when you need it.
final currentID = await FlutterInsider.Instance.getInsiderID();
print('Insider ID after login attempt: $currentID');
} else {
print('Logged in as $insiderID');
}Available methods
Identity
loginAsync
Identifies the user with one or more identifiers and resolves to the merged Insider ID. This is the awaitable form of login with insiderIDResult.
Name | Type | Required | Description |
|---|---|---|---|
identifiers | FlutterInsiderIdentifiers | Yes | Email, phone number, user ID, or custom identifiers. An empty set is not sent and resolves to null. |
timeout | Duration | No | How long to wait for the native SDK. Default: Duration(seconds: 10). Must be positive. |
Signature:
Future<String?> loginAsync(
FlutterInsiderIdentifiers identifiers, {
Duration timeout = const Duration(seconds: 10),
})Example:
final identifiers = FlutterInsiderIdentifiers()
..addEmail('user@example.com')
..addUserID('user-001');
final insiderID = await FlutterInsider.Instance.getCurrentUser()?.loginAsync(identifiers);
print('Insider ID: $insiderID');Keep using login(identifiers, insiderIDResult: ...) when you want the callback style. loginAsync sends the same request as login with a callback.
logout
Logs the current user out and keeps the existing Insider ID.
Signature:
Future<void> logout()Example:
await FlutterInsider.Instance.getCurrentUser()?.logout();logoutResettingInsiderIDAsync
Logs the user out, resets the Insider ID, and resolves to the new one. This is the awaitable form of logoutResettingInsiderID with insiderIDResult.
Name | Type | Required | Description |
|---|---|---|---|
additionalIdentifiers | List<FlutterInsiderIdentifiers>? | Yes | Identifiers attached to the new anonymous identity. Pass null for none. |
timeout | Duration | No | How long to wait for the native SDK. Default: Duration(seconds: 10). Must be positive. |
Signature:
Future<String?> logoutResettingInsiderIDAsync(
List<FlutterInsiderIdentifiers>? additionalIdentifiers, {
Duration timeout = const Duration(seconds: 10),
})Example:
final newInsiderID = await FlutterInsider.Instance
.getCurrentUser()
?.logoutResettingInsiderIDAsync(null);
print('New Insider ID: $newInsiderID');In-app messages and deep links
disableInAppMessages
Suppresses in-app messages until you call enableInAppMessages.
Signature:
Future<void> disableInAppMessages()Example:
await FlutterInsider.Instance.disableInAppMessages();enableInAppMessages
Re-enables in-app messages after a previous disableInAppMessages call.
Signature:
Future<void> enableInAppMessages()Example:
await FlutterInsider.Instance.disableInAppMessages();
// Show your own full-screen flow, then
await FlutterInsider.Instance.enableInAppMessages();handleUniversalLink
Lets the Insider SDK process a deep link or universal link. Call it from your link handler.
Name | Type | Required | Description |
|---|---|---|---|
url | String | Yes | The link to hand to the SDK. |
Signature:
Future<void> handleUniversalLink(String url)Example:
await FlutterInsider.Instance.handleUniversalLink('https://example.com/campaign?ins=1');App Cards tracking
The App Cards tracking methods return a Future<void> that completes when the native SDK call has returned. See Configure app cards for the card model and the retrieval methods.
Method | Signature |
|---|---|
FlutterInsiderAppCards.view | Future<void> view(InsiderAppCard appCard) |
FlutterInsiderAppCards.click | Future<void> click(InsiderAppCard appCard) |
FlutterInsiderAppCards.clickButton | Future<void> clickButton(InsiderAppCardButton button) |
InsiderAppCard.view | Future<void> view() |
InsiderAppCard.click | Future<void> click() |
InsiderAppCardButton.click | Future<void> click() |
Example:
final appCards = FlutterInsider.Instance.appCards;
try {
final response = await appCards.getCampaigns();
for (final card in response?.appCards ?? <InsiderAppCard>[]) {
await card.view();
}
} on InsiderAppCardsException catch (e) {
print('Failed to get campaigns: ${e.error.code}');
}Migration notes
Call sites compile unchanged. A Future<void> returned from a method you used to call as void is dropped. Two things can change in your project:
Lint hits: With unawaited_futures or discarded_futures enabled, the linter reports every existing call to one of these methods. Either await the call or wrap it in unawaited(...) from dart:async.
Test doubles: A fake or mock that overrides one of these methods with a void body, or an unstubbed mock, now has to return a Future. Return Future.value() for Future<void>, and Future.value(null) or an ID string for Future<String?>.
To keep the fire-and-forget behavior explicitly, wrap the call in unawaited:
import 'dart:async';
// Keep the fire-and-forget behaviour explicitly
unawaited(FlutterInsider.Instance.handleUniversalLink(url));Full integration example
The following example signs a user in and out. It keeps in-app messages away from the sign-in screen and handles a login that returns no Insider ID.
import 'package:flutter_insider/flutter_insider.dart';
import 'package:flutter_insider/src/identifiers.dart';
Future<void> signIn(String email, String userID) async {
final user = FlutterInsider.Instance.getCurrentUser();
if (user == null) {
return;
}
// Keep in-app messages out of the way while the sign-in screen is up
await FlutterInsider.Instance.disableInAppMessages();
final identifiers = FlutterInsiderIdentifiers()
..addEmail(email)
..addUserID(userID);
final insiderID = await user.loginAsync(
identifiers,
timeout: const Duration(seconds: 15),
);
if (insiderID == null) {
final currentID = await FlutterInsider.Instance.getInsiderID();
print('Login not confirmed; current Insider ID: $currentID');
} else {
print('Logged in as $insiderID');
}
// Resumes only after the native call has returned
await FlutterInsider.Instance.enableInAppMessages();
}
Future<void> signOut() async {
final user = FlutterInsider.Instance.getCurrentUser();
if (user == null) {
return;
}
final newInsiderID = await user.logoutResettingInsiderIDAsync(null);
print('Anonymous Insider ID: $newInsiderID');
}Awaiting SDK calls lets you run Flutter SDK calls in order and read the merged Insider ID when a login or logout finishes. For the individual methods, see the user object, block in-app messages, handle universal links, and configure app cards pages.