adstart_flutter_sdk 0.1.0
adstart_flutter_sdk: ^0.1.0 copied to clipboard
Flutter SDK for Adstart event collection.
Adstart Flutter SDK #
Flutter wrapper for Adstart event collection.
The SDK sends session, customer identification, catalog, search, wishlist,
cart, checkout, engagement, and custom events. It persists a deviceId across
app launches and creates a new sessionId for every call to init().
Installation #
Add the package from pub.dev:
flutter pub add adstart_flutter_sdk
Or add it directly to pubspec.yaml:
dependencies:
adstart_flutter_sdk: ^0.1.0
Then run flutter pub get.
Installation from Git #
To test an unreleased branch, use the Git repository:
dependencies:
adstart_flutter_sdk:
git:
url: https://github.com/adstart-team/adstart-flutter-sdk.git
ref: branch-or-commit
Usage #
Configure and initialize the Firebase project for the current business before
calling AdstartSdk.init(). Follow the FlutterFire setup for Android and iOS,
including the APNs configuration required by Firebase Cloud Messaging on iOS.
import 'package:firebase_core/firebase_core.dart';
import 'package:adstart_flutter_sdk/adstart_flutter_sdk.dart';
await Firebase.initializeApp();
final adstart = AdstartSdk(hashedId: 'your-hashed-id', debug: true);
final sessionId = await adstart.init(
onPushEvent: (event) {
switch (event.type) {
case AdstartPushEventType.received:
// The app owns foreground presentation, if any.
debugPrint('Push received: ${event.notification?.title}');
case AdstartPushEventType.opened:
// Forward the destination to Navigator, go_router, or another router.
final deepLink = event.deepLink;
if (deepLink != null) {
debugPrint('Open $deepLink');
}
}
},
);
await adstart.addProductToWishlist(
WishlistProductEventParams(
currency: 'BRL',
items: [
AdstartItem(
itemId: 'product-123',
itemName: 'Product name',
price: 249.90,
),
],
source: 'product_detail',
),
);
await adstart.removeProductFromWishlist(
WishlistProductEventParams(
currency: 'BRL',
items: [AdstartItem(itemId: 'product-123', price: 249.90)],
),
);
await adstart.viewWishlist(
const WishlistViewEventParams(source: 'account_menu'),
);
await adstart.addProductToCart(
EcommerceEventParams(
currency: 'BRL',
items: [AdstartItem(itemId: 'product-123', price: 249.90)],
),
);
await adstart.removeProductFromCart(
EcommerceEventParams(
currency: 'BRL',
items: [AdstartItem(itemId: 'product-123', price: 249.90)],
),
);
final cart = EcommerceEventParams(
currency: 'BRL',
items: [
AdstartItem(itemId: 'product-123', price: 249.90, quantity: 2),
],
);
await adstart.viewCart(cart);
final checkout = CheckoutEventParams(
currency: 'BRL',
items: cart.items,
coupon: 'WELCOME10',
);
await adstart.addPaymentInfo(checkout);
await adstart.addShippingInfo(checkout);
await adstart.initiateCheckout(checkout);
await adstart.productViewed(
EcommerceEventParams(
currency: 'BRL',
items: [
AdstartItem(
itemId: 'product-123',
itemName: 'Product name',
price: 249.90,
itemBrand: 'Brand name',
itemCategory: 'Category name',
),
],
),
);
await adstart.categoryViewed(name: 'Category name');
await adstart.brandViewed(name: 'Brand name');
await adstart.searchedFor(searchTerm: 'black shoes');
await adstart.trackTimeOnPage(TimeOnPageEventParams(seconds: 45));
await adstart.trackScrollDepth(ScrollDepthEventParams(depth: 50));
await adstart.customerIdentified(
CustomerIdentificationEventParams(
email: 'customer@example.com',
customerExternalId: 'customer-123',
),
);
await adstart.trackCustomEvent(
'product_compared',
eventParams: <String, Object?>{
'productIds': <String>['product-123', 'product-456'],
'source': 'comparison_screen',
},
);
adstart.dispose();
Call init() before sending events. All events sent after
initialization reuse the active session's hashedId, deviceId, sessionId,
and environment metadata.
Ecommerce and catalog events #
AdstartItem is the shared public item model for wishlist, cart, checkout, and
product view events. Each item requires a finite, non-negative price, a
positive quantity that defaults to 1, and at least one non-empty itemId or
itemName. It also supports affiliation, item-level coupon and discount,
position, brand, five category levels, list information, variant, and location.
EcommerceEventParams requires a non-empty item list and a three-letter
currency code. Currency is trimmed and normalized to uppercase. The SDK derives
the emitted value by summing price * quantity for all items with decimal
accumulation; callers cannot provide a conflicting value.
Use WishlistProductEventParams when the optional source is useful. Use
CheckoutEventParams for payment, shipping, and checkout events; its coupon
is optional and is omitted from the payload when absent or blank.
The public methods preserve Adstart event names:
| Method | eventType |
Required parameters |
|---|---|---|
addProductToWishlist |
added_to_wishlist |
currency, items |
removeProductFromWishlist |
removed_from_wishlist |
currency, items |
addProductToCart |
added_to_cart |
currency, items |
removeProductFromCart |
removed_from_cart |
currency, items |
viewCart |
view_cart |
currency, items |
addPaymentInfo |
added_payment_info |
currency, items |
addShippingInfo |
added_shipping_info |
currency, items |
initiateCheckout |
checkout_initiated_by |
currency, items |
productViewed |
product_viewed |
currency, items |
categoryViewed |
category_viewed |
name |
brandViewed |
brand_viewed |
name |
searchedFor |
search |
searchTerm |
searchedFor emits the term as eventParams.search_term. The Adstart event
ingestion contract must include the search event type before production can
accept this event.
Customer identification #
Use customerIdentified after init() to associate identity data with the
current device. At least one non-empty email, phone, or document is
required. name, customerExternalId, and gender are optional. gender,
when provided, is typed as CustomerGender.male or CustomerGender.female.
Only these canonical identity fields are sent in eventParams; session and
device identifiers remain in the common event envelope.
Push notifications #
init() never opens the notification permission prompt. When permission is
already authorized or provisional, it silently obtains the FCM token and
registers it with Adstart. A missing Firebase configuration, a null token, or a
registration failure does not make a successful session_started fail.
Ask for permission only from an explicit user action:
await adstart.requestPushPermission();
Call init() first so the registration can reuse the SDK's persistent
deviceId and configured hashedId. The token is sent to
POST https://events.adstart.cloud/v1/app-push/register together with the
Firebase appId. Authorized and provisional results are registered. Denied
and not-determined results are not registered.
The SDK observes FirebaseMessaging.onTokenRefresh and synchronizes new tokens.
It stores only a SHA-256 token fingerprint for deduplication, never the complete
token, and debug logs never include the complete token. Call dispose() when
the SDK is no longer used so the refresh listener is canceled.
To delete the token from the local Firebase installation:
await adstart.disablePushNotifications();
In version 0.0.8 this method only calls FirebaseMessaging.deleteToken(). It
does not call a backend revoke endpoint.
Receiving and opening push notifications #
Pass an optional onPushEvent callback to init() to receive typed push
events. AdstartPushEventType.received is emitted for messages observed while
the app is in the foreground. The SDK does not display these messages; the app
owns any foreground notification UI, Android notification channels, icons, and
iOS presentation options. The normalized event.notification contains the
optional title and body needed for foreground presentation, while event.data
contains the custom FCM data fields.
AdstartPushEventType.opened is emitted when the user opens a notification
from the background or launches the app by opening it from the terminated
state. Openings without a deep link are still emitted with deepLink == null.
The SDK extracts an absolute URI from the adstart_deep_link data field but
never navigates or opens it. The integrating app must validate that the URI is
an allowed destination before forwarding it to its router.
Use these optional FCM data fields:
{
"adstart_message_id": "unique-message-id",
"adstart_deep_link": "posthaus://product/123"
}
adstart_message_id is preferred for deduplicating openings. If it is absent,
the SDK uses the Firebase message ID and then a fingerprint of the message
data.
Background or terminated delivery does not guarantee a received callback.
In those states Android and iOS can display a notification payload without
running the main Flutter isolate. This version does not install a global
FirebaseMessaging.onBackgroundMessage handler. Delivery of data-only
background messages is platform-dependent and outside this callback contract.
Call dispose() when the SDK is no longer used. It cancels the foreground,
opening, and token refresh listeners.
Custom events #
Use trackCustomEvent for business events that are not part of the SDK's
built-in event catalog. The eventKey is required, must start with a lowercase
letter, may contain only lowercase letters, numbers, and underscores, and may
contain at most 64 characters. Built-in Adstart event names are reserved.
eventParams is optional and defaults to an empty JSON object. Its encoded JSON
must not exceed 64 KiB in UTF-8.
Screen engagement #
Flutter does not expose a reliable global definition of a page or its scroll
depth. The application must measure engagement for each native screen and call
trackTimeOnPage with the elapsed whole seconds (from 1 through 1800) and
trackScrollDepth with a percentage (from 0 through 100). The web tracker uses
the 25, 50, 75, and 100 percent milestones.
Content rendered inside a WebView has its own document lifecycle and scroll position. Measure it in the web content and forward the resulting seconds or depth to these methods; the SDK does not install global navigation, scroll, or WebView listeners.