Skip to content
ABTO Guide

Integration guide

Flutter / Dart

Collects user behavior and Custom Events in Flutter apps. Installed from pub.dev.

Client SDKRuns in the browser or a mobile app (Event Key)

The Flutter SDK is publicly available on pub.dev and requires Dart 3.4 or later. Its pure Dart (dart:io) core supports Flutter on iOS, Android, desktop, and server runtimes. Use the Browser JavaScript SDK for Flutter Web.

After installing the SDK, add the dependency used by the persistent-store adapter below:

Terminal window
flutter pub add shared_preferences
import 'package:abto/abto.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesStore implements AbtoKeyValueStore {
SharedPreferencesStore(this._preferences);
final SharedPreferences _preferences;
@override
String? get(String key) => _preferences.getString(key);
@override
void set(String key, String value) {
_preferences.setString(key, value);
}
}
final preferences = await SharedPreferences.getInstance();
final abto = AbtoClient(
AbtoConfig(
projectKey: 'ek-abto-…',
endpoint: 'https://api.abto.app/v1/collect/events',
environment: AbtoEnvironment.production,
),
store: SharedPreferencesStore(preferences),
);
abto.identify('user-123', 'tenant-123');
abto.capture('checkout_completed', properties: {'order_id': 'order-123'});
await abto.flush();

Use only an Event Key (ek-abto-…) in the client. Never put a Calling Key or provider key in the app. The second tenantId is optional; call abto.reset() on sign-out to clear the user and tenant context and create a new device_id and session.

Adapting the app’s existing shared_preferences storage to AbtoKeyValueStore preserves the current device_id across ordinary app restarts and replaces it after reset. If you omit the store, the in-memory identity creates a different device_id after an app restart. Forward abto.deviceId as x-abto-device-id with the related server request to connect app events with Gateway calls. Failed deliveries return to the internal buffer and retry on the next flush.

The Mobile SDK does not call a model or the Gateway directly. Send deviceId and featureId to your application backend. The backend must validate them, pass them through Server SDK context, and expose the resulting x-abto-request-id in its response. The backend side is covered in Node / Server JavaScript and Python.

final trace = abto.startLlmTrace(
featureId: 'resume.make',
taskType: 'draft_generation',
surface: 'editor',
);
trace.submitPrompt(prompt: promptText, language: 'en');
final backendResponse = await callBackend(
deviceId: abto.deviceId,
featureId: trace.featureId,
prompt: promptText,
);
trace.attachRequestIdFromHeaders(backendResponse.headers);
trace.markResponseVisible(responseId: 'resp-123', timeToVisibleMs: 1200);
trace.captureOutcome(AbtoResponseInteraction.copied, responseId: 'resp-123');

AbtoResponseInteraction exposes the 12 canonical response actions. String calls remain source-compatible during the 0.x line and are validated at runtime. Unsupported strings are warned and dropped before enqueueing; use a Custom Event for product-specific actions.

callBackend stands for your application’s existing networking function. Response events captured after attachRequestIdFromHeaders() carry the same $request_id as the Gateway call.

  • Custom Properties passed to capture() do not go through Browser DOM masking. Do not put secrets or raw personal data in properties.
  • LLM trace prompt and response helpers record metadata_only information such as content length instead of the raw text.
  • Event names cannot be empty, start with $, or exceed 200 UTF-16 code units. Custom Properties starting with $ are omitted.
  • batchSize accepts 1 through 100, defaults to 20, and the default flush interval is 5 seconds.
  • Metric value must be finite with at most 38 integer digits and 12 fractional digits; scale is limited to 16 characters. An invalid metric is omitted while the event is still sent.
  • The in-memory delivery buffer holds at most 1,000 events and drops the oldest on overflow. It retries 408, 429, 5xx, and per-event retry results up to 5 attempts or 30 minutes after first enqueueing, with jittered exponential backoff capped at 2 minutes.
  • Delivery failures are not thrown into the app. The memory buffer does not survive process termination, so call flush() at the app lifecycle’s background transition.

How to name events and design Custom Properties is in Event design.