#
SDK Gravity Field (Flutter)
Gravity Field SDK — это лёгкий клиент для интеграции мобильных приложений (iOS / Android) с платформой персонализации и A/B-тестирования. Он позволяет запускать кампании без необходимости реализовывать собственную логику таргетинга, выбора и аналитики.
SDK работает по принципу "тонкого клиента": все решения принимает сервер (бекенд Gravity Field), а SDK:
- передаёт контекст текущего экрана,
- получает кампании и их содержимое в виде JSON или встроенных шаблонов,
- активирует показ
inline
иin-app
кампаний - трекает взаимодействия пользователя (просмотры, клики, покупки и т.д.),
- и помогает фиксировать конверсии для аналитики и обучения моделей.
📦 Эта документация предназначена для мобильных разработчиков и инженеров, которые интегрируют SDK в приложение.
#
Добавление библиотеки в проект
- Из корня проекта вызовите команду:
flutter pub add gravity_sdk
- После добавления плагина в файле
pubspec.yaml
появится строка с зависимостью:
dependencies:
gravity_sdk: ^0.9.1 # Замените на актуальную версию
- Добавьте импорт:
import 'package:gravity_sdk/gravity_sdk.dart';
Требования к версиям:
- Dart SDK:
>=3.6.0
- Flutter:
>=1.17.0
Для iOS:
Добавьте в файл ios/Runner/Info.plist
ключ NSUserTrackingUsageDescription
для запроса разрешения на отслеживание:
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
#
Быстрый старт
Этот раздел проведет вас через основные шаги интеграции SDK, от инициализации до запуска вашей первой кампании.
#
Шаг 1: Инициализация SDK
Сначала необходимо инициализировать SDK с вашим apiKey
и section
. Это лучше всего делать в функции main()
вашего приложения. Дополнительно мы подключим GravityEventCallback
, чтобы видеть события SDK в консоли.
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:gravity_sdk/gravity_sdk.dart';
void main() async {
// Обязательно до runApp
WidgetsFlutterBinding.ensureInitialized();
// Инициализация SDK
await GravitySDK.instance.initialize(
apiKey: 'YOUR_API_KEY',
section: 'YOUR_SECTION_ID',
// Обработка действий
gravityEventCallback: (event) {
print('Gravity SDK Event: ${event.runtimeType}');
// Обработка перехода по внешней ссылке
if (event is FollowUrlEvent) {
launchUrl(Uri.parse(event.url), mode: LaunchMode.externalApplication);
}
// Обработка перехода по диплинку
if (event is FollowDeeplinkEvent) {
// Здесь ваша логика навигации по диплинкам.
// Например, с использованием GoRouter:
// context.go(event.deeplink);
print('Follow Deeplink: ${event.deeplink}');
}
},
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// ...
}
}
Чтобы интерактивные элементы в ваших In-App кампаниях (например, кнопки "Купить" или "Подробнее") работали, необходимо обрабатывать события, которые SDK отправляет в gravityEventCallback. Без этой обработки клики по ссылкам и диплинкам не приведут к навигации.
В примере инициализации показано, как обрабатывать FollowUrlEvent для открытия внешних ссылок и FollowDeeplinkEvent для внутренней навигации в приложении. Убедитесь, что вы реализовали эту логику.
Для открытия URL мы использовали пакет url_launcher. Не забудьте добавить его в ваш pubspec.yaml:
```yaml
dependencies:
url_launcher: ^6.3.1
Подробнее о всех возможных событиях читайте в разделе
#
Шаг 2: Идентификация пользователя
Когда пользователь входит в систему, важно связать его действия, совершенные анонимно, с его постоянным профилем. Для этого после авторизации отправляется LoginEvent
. Это рекомендуемый способ идентификации.
// Вызывается после успешной авторизации пользователя
void onUserLoggedIn(BuildContext context, String userCustomId) {
final loginEvent = LoginEvent(cuid: userCustomId, cuidType: 'mySystemUserId');
GravitySDK.instance.triggerEvent(
context: context,
events: [loginEvent],
pageContext: PageContext(
type: ContextType.other,
data: [],
location: 'app://login',
),
);
}
Отправка LoginEvent
"склеивает" анонимный профиль с профилем авторизованного пользователя, сохраняя всю историю его действий.
Подробнее о различных способах идентификации читайте в разделе
#
Шаг 3: Отслеживание просмотров экранов
Gravity Field принимает решение о показе персонализированного контента на основе контекста страницы, который передаёт SDK. Давайте отследим просмотр главной страницы.
// В коде виджета вашей главной страницы
void trackHomepageView(BuildContext context) {
GravitySDK.instance.trackView(
context: context,
pageContext: PageContext(
type: ContextType.homepage,
data: [],
location: 'app://homepage',
),
);
}
Если для этого события настроена in-app кампания, SDK автоматически покажет ее.
#
Шаг 4: Отслеживание событий
Теперь отследим добавление товара в корзину. Это событие может запустить кампанию с товарными рекомендациями (например, "с этим товаром покупают").
void trackAddToCart(BuildContext context, String productId) {
final event = AddToCartEvent(
value: 99.99,
productId: productId,
quantity: 1,
currency: 'RUB',
);
GravitySDK.instance.triggerEvent(
context: context,
events: [event],
pageContext: PageContext(
type: ContextType.product,
data: [productId],
location: 'app://product/$productId',
),
);
}
#
Шаг 5: Отображение inline-кампаний
Для отображения рекомендаций прямо в верстке страницы (например, блок "Персональные рекомендации") используйте виджет GravityInlineWidget
.
// В методе build вашего виджета
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Inline Recommendations')),
body: ListView(
children: [
// ... другие виджеты
GravityInlineWidget(
selector: 'homepage-recommendations',
height: 250, // Задайте высоту для блока
),
// ... другие виджеты
],
),
);
}
Виджет сам загрузит и отобразит релевантный контент по селектору homepage-recommendations
.
#
Инициализация и конфигурация
#
initialize()
Основной метод для настройки SDK. Вызывается один раз при старте приложения.
Future<void> initialize({
required String apiKey,
required String section,
ProductWidgetBuilder? productWidgetBuilder,
GravityEventCallback? gravityEventCallback,
});
apiKey
(required): Ваш уникальный ключ API.section
(required): Идентификатор секции вашего проекта.productWidgetBuilder
: Опциональный билдер для кастомизации виджетов продуктов. (См. раздел8. Кастомизация UI ).gravityEventCallback
: Опциональный колбэк для получения уведомлений о событиях SDK. (См. разделОбработка обратных вызовов (Callbacks) ).
#
setOptions()
Позволяет задать глобальные настройки для всех последующих запросов.
void setOptions({
Options? options,
ContentSettings? contentSettings,
String? proxyUrl,
});
options
: Настройки для управления поведением запросов.contentSettings
: Настройки для управления получаемым контентом.proxyUrl
: URL прокси-сервера для отправки запросов.
Пример:
GravitySDK.instance.setOptions(
options: Options(
isReturnUserInfo: true, // Возвращать информацию о пользователе в ответах
isImplicitImpression: true, // Автоматически отправлять событие показа
),
contentSettings: ContentSettings(
skusOnly: false, // Возвращать полную информацию о продуктах, а не только SKU
fields: ['name', 'price', 'imageUrl'], // Запросить конкретные поля
),
);
#
Идентификация пользователей
Идентификация пользователя — фундамент для персонализации, аналитики и построения омниканальных профилей в Gravity Field.
Без корректной идентификации невозможно:
- Связывать действия пользователя между сеансами и устройствами,
- Персонализировать рекомендации,
- Точно собирать аналитику,
- Строить сценарии персонализации и сегментацию.
#
Варианты идентификации
Gravity Field позволяет автоматически или вручную управлять идентификацией, гибко подстраиваясь под архитектуру вашего проекта.
Gravity Field UID (автоматическая идентификация)
Когда использовать:
- Когда нет собственного механизма учёта пользователей и сессий
Как работает:
- При первом запросе к Gravity Field (например,
trackView(...)
), если нет сохранённых идентификаторов, Gravity Field автоматически создаёт уникальные идентификаторы:uid
— уникальный идентификатор пользователя, автоматически сгенерированный Gravity Fieldses
— ID сессии, автоматически сгенерированный Gravity Field
- Эти значения возвращаются в ответе и сохраняются.
- SDK будет использовать их во всех следующих запросах.
- Сессия живёт 30 минут и продлевается при активности пользователя.
После авторизации:
- Чтобы связать анонимный профиль с авторизованным, вызовите событие
login
черезtriggerEvent(...)
. - Gravity Field объединит поведение до и после логина.
Пример отправки LoginEvent:
// Вызывается после успешной авторизации
void onUserLoggedIn(BuildContext context, String userCustomId) {
final loginEvent = LoginEvent(
cuid: userCustomId,
cuidType: 'mySystemUserId' // Укажите тип вашего идентификатора
);
GravitySDK.instance.triggerEvent(
context: context,
events: [loginEvent],
pageContext: PageContext(
type: ContextType.other,
data: [],
location: 'app://login',
),
);
}
Custom user id (идентификация через ID приложения)
Когда использовать:
- Если у пользователя в приложении есть собственный ID и логика управления сессиями.
Как работает:
- Разработчик вызывает
setUser(...)
.userId
— ваш собственный ID пользователя (custom ID);sessionId
— ID текущей сессии.
- SDK использует эти значения в каждом запросе.
💡 Если вы передаёте
userId
, вы обязаны также передатьsessionId
. SDK не может создатьsessionId
автоматически в этом режиме.
#
setUser(...)
Позволяет явно установить параметры пользователя и сессии, которые будут автоматически включаться во все запросы SDK к API (/visit
, /event
, /choose
, /engagement
).
void setUser(String customId, String sessionId);
Пример:
// Вызывается в самом начале работы с приложением, если ID уже известны
GravitySDK.instance.setUser('user-from-my-system-42', 'session-from-my-system-xyz');
#
Как SDK работает с идентификаторами
SDK автоматически управляет идентификаторами пользователя и сессии, опираясь на то, что уже сохранено локально. Используется следующая логика:
- Если вызван
setUser(...)
— SDK использует переданныеuserId
иsessionId
(режим: custom user ID). - Если
setUser(...)
не вызывался, но уже естьuid
иses
— SDK использует их (режим: Gravity Field UID). - Если ни один ID не сохранён — SDK отправляет запрос без идентификаторов. Gravity Field создаёт
uid
иses
, SDK сохраняет их и использует в дальнейшем.
#
Передача контекста
Gravity Field принимает решение о показе персонализированного контента на основе контекста страницы, который передаёт SDK.
Контекст страницы позволяет Gravity Field понять:
- Где сейчас находится пользователь в приложении
- Какие товары или категории он просматривает
- В каком регионе или в каких условиях пользователь находится
#
trackView(...)
Отправляет событие просмотра экрана. В ответ может прийти кампания для показа.
Future<void> trackView({
required BuildContext context,
required PageContext pageContext,
});
context
:BuildContext
текущего экрана.pageContext
: Контекст страницы, который описывает, где находится пользователь.
Пример:
// На экране продукта
GravitySDK.instance.trackView(
context: context,
pageContext: PageContext(
type: ContextType.product,
data: ['product-sku-123'], // SKU продукта
location: 'app://product/123',
),
);
#
PageContext
PageContext
— ключевая модель для описания местоположения и контекста пользователя в приложении.
class PageContext {
final ContextType type;
final List<String> data;
final String location;
final String? lng;
final Map<String, String>? utm;
final Map<String, Object>? attributes;
}
type
: Тип страницы (ContextType
).data
: Массив строк с данными, зависящими от типа.location
: Уникальный идентификатор местоположения (URL, deeplink, название экрана).lng
: Язык.utm
: UTM-метки.attributes
: Дополнительные атрибуты.
Примеры PageContext
для разных ContextType
:
homepage
:PageContext(type: ContextType.homepage, data: [], location: 'app://home')
product
:PageContext(type: ContextType.product, data: ['sku-123'], location: 'app://product/123')
cart
:PageContext(type: ContextType.cart, data: ['sku-123', 'sku-456'], location: 'app://cart')
category
:PageContext(type: ContextType.category, data: ['Электроника'], location: 'app://category/electronics')
search
:PageContext(type: ContextType.search, data: ['запрос'], location: 'app://search?q=запрос')
other
:PageContext(type: ContextType.other, data: [], location: 'app://about')
💡 Типы контекста и соответсвующие для них data описаны здесь: Page context
#
Трекинг событий
Трекинг событий позволяет отправлять информацию о действиях пользователя — таких как покупка, добавление товара в корзину, авторизация и другие события. Эти данные используются Gravity Field для аналитики, построения сегментов и запуска кампаний, активируемых по событиям.
Трекинг событий применяется, когда кампании должны срабатывать не по просмотру страницы, а по пользовательскому действию, и также для сбора аналитики.
События фиксируются для того, чтобы измерить поведение пользователя в разных вариациях кампаний: например, узнать, какой из вариантов чекаута приводит к более высокой конверсии. Это позволяет анализировать эффективность персонализации и принимать решения на основе данных.
Платформа поддерживает как системные (предопределённые) события, так и произвольные пользовательские события.
- Системные события — это события с зафиксированными
type
, которые платформа распознаёт и может использовать для активации кампаний или анализа (например,purchase
,add_to_cart
,login
). - Пользовательские события — разработчик может отправлять любые события с кастомным
type
. Такие события будут использоваться для аналитики и построения собственных отчётов или логики в Gravity Field.
#
triggerEvent(...)
Отправляет одно или несколько событий. В ответ, как и в случае с trackView, может прийти кампания для показа.
Future<void> triggerEvent({
required BuildContext context,
required List<TriggerEvent> events,
required PageContext pageContext,
});
context
:BuildContext
текущего экрана.events
: Список событий для отправки. SDK предоставляет множество готовых классов событий (PurchaseEvent, LoginEvent и др.).pageContext
: Контекст страницы.
Ниже приведены примеры кода для отслеживания наиболее распространенных бизнес-событий с помощью triggerEvent().
#
Покупка (PurchaseEvent
)
Событие отправляется после успешного завершения заказа.
final purchaseEvent = PurchaseEvent(
uniqueTransactionId: 'ORDER-12345', // Уникальный ID транзакции
value: 2550.75,
currency: 'RUB',
cart: [
CartItem(productId: 'sku-123', quantity: 1, itemPrice: 100.50),
CartItem(productId: 'sku-456', quantity: 1, itemPrice: 50.00),
CartItem(productId: 'sku-abc-1', quantity: 1, itemPrice: 1500.00),
CartItem(productId: 'sku-def-2', quantity: 2, itemPrice: 525.375),
],
);
GravitySDK.instance.triggerEvent(
context: context,
events: [purchaseEvent],
pageContext: PageContext(
type: ContextType.other,
data: [],
location: 'app://checkout/success',
),
);
#
Добавление в корзину (AddToCartEvent
)
Отправляется, когда пользователь добавляет товар в корзину.
final addToCartEvent = AddToCartEvent(
value: 1500.00,
productId: 'sku-abc-1',
quantity: 1,
currency: 'RUB',
);
GravitySDK.instance.triggerEvent(
context: context,
events: [addToCartEvent],
pageContext: PageContext(
type: ContextType.product,
data: ['sku-abc-1'],
location: 'app://product/sku-abc-1',
),
);
#
Вход в систему (LoginEvent
)
Отправляется после успешной аутентификации пользователя.
final loginEvent = LoginEvent(
// Передайте один из идентификаторов
cuid: 'customer-12345',
cuidType: 'mySystemUserId',
// hashedEmail: '...' // или хешированный email
);
GravitySDK.instance.triggerEvent(
context: context,
events: [loginEvent],
pageContext: PageContext(
type: ContextType.other,
data: [],
location: 'app://login',
),
);
#
Кастомное событие (CustomEvent
)
Для отслеживания любых других действий, не покрытых стандартными событиями.
final customEvent = CustomEvent(
type: 'survey-completed-v1', // Уникальный тип события
name: 'Опрос пройден', // Человекочитаемое имя
properties: {
'surveyId': 'summer-2025-feedback',
'rating': '5',
},
);
GravitySDK.instance.triggerEvent(
context: context,
events: [customEvent],
pageContext: PageContext(
type: ContextType.other,
data: [],
location: 'app://survey/summer-2025-feedback',
),
);
Подробное описание всех доступных событий и их параметров смотрите в разделе TriggerEvent
)
#
Работа с контентом
#
Inline-блоки (встраиваемые кампании в UI)
Кампания встраивается в существующую структуру экрана приложения — но только если условия показа, заданные на платформе Gravity Field, выполнены.
GravityInlineWidget
SDK запрашивает кампанию по selector
, передаёт контекст текущей страницы, и если сервер вернёт релевантный контент — он будет отображён в заданном UI-элементе.
GravityInlineWidget({
required String selector,
String? placeholderId,
double? width,
double? height,
Map<String, dynamic>? attributes,
})
selector
задается в настройках кампании, в личном кабинете Gravity Field.
placeholderId
задается в настройках вариации кампании, в лично кабинете Gravity Field
💡 Если условия показа не выполнены (например, пользователь не входит в целевую группу), блок не будет отрисован. ⚠️ Несмотря на то что параметр height является опциональным, его необходимо передавать, иначе виджет может "схлопнуться" до нулевой высоты и не будет виден.
📌 Подходит для: баннеров, товарных рекомендаций, УТП блоков.
Пример:
GravityInlineWidget(
selector: 'homepage-recommendations',
// Опционально: задайте размеры
height: 250,
// Опционально: передайте контекст, если он отличается от общего
pageContext: PageContext(type: ContextType.homepage, data: [], location: 'app://home'),
)
#
Кастомизация внешнего вида товарных рекомендаций
Чтобы карточки товаров в кампаниях выглядели в стиле вашего приложения, создайте свой класс, наследующий ProductWidgetBuilder
, и передайте его экземпляр в initialize
.
// 1. Создайте свой билдер
class CustomProductWidgetBuilder extends ProductWidgetBuilder {
@override
Widget build({
required BuildContext context,
required Slot product,
required CampaignContent content,
required Campaign campaign,
}) {
// Ваша кастомная верстка карточки товара
return Card(
child: ListTile(
leading: Image.network(product.item.imageUrl ?? ''),
title: Text(product.item.name),
subtitle: Text('${product.item.price} RUB'),
onTap: () {
// При клике важно отправлять событие взаимодействия
GravitySDK.instance.sendProductEngagement(
ProductClickEngagement(product, content, campaign)
);
// ... и выполнять переход на экран товара
},
),
);
}
}
// 2. Передайте его в initialize
await GravitySDK.instance.initialize(
apiKey: 'YOUR_API_KEY',
section: 'YOUR_SECTION_ID',
productWidgetBuilder: CustomProductWidgetBuilder(),
);
#
Отображение JSON-кампаний (ручной рендеринг)
В некоторых случаях кампания может возвращать не готовый UI, а чистые данные в формате JSON. Это дает вам полный контроль над тем, как этот контент будет выглядеть и вести себя в вашем приложении. Вы несете ответственность за рендеринг UI на основе полученных данных.
#
getContentBySelector(...)
Основной способ получить такие данные — использовать метод getContentBySelector
.
Пример:
// Получаем контент для инлайнового блока на главной
final response = await GravitySDK.instance.getContentBySelector(
selector: 'homepage-inline-banner',
pageContext: PageContext(type: ContextType.homepage, data: [], location: 'app://home'),
);
// Используем полученные данные для отображения
if (response.data.isNotEmpty) {
final campaign = response.data.first;
// ... ваша логика отображения
}
#
Событийная модель
Вы также можете получать JSON-кампании, которые активируются в ответ на события (trackView
, triggerEvent
). Для этого подпишитесь на gravityEventCallback
и отлавливайте событие ContentLoadEvent
. Этот подход полезен, когда кампания должна появиться не по прямому запросу, а как реакция на действие пользователя.
await GravitySDK.instance.initialize(
// ... другие параметры
gravityEventCallback: (event) {
if (event is ContentLoadEvent) {
// Проверяем, что это нужная нам кампания
if (event.type == 'LoadEvent' && event.selector == 'custom_popup') {
// Здесь ваша логика для обработки и отображения данных из event.content
print('Получена JSON-кампания: ${event.content.contentId}');
}
}
},
);
#
Трекинг взаимодействий (engagement) с кампанией
Engagement-события для кампаний позволяют Gravity Field анализировать поведение пользователя в деталях:
- Какой контент был показан и как он повлиял на поведение
- Какая вариация кампании эффективнее: какой баннер чаще просматривают, на какой товар чаще кликают, что влияет на покупку
- Понимание того, какие элементы UI реально видны (
WRIMP
), позволяет фильтровать «слепые зоны» и улучшать UX - Фиксация кликов и показов даёт системе обратную связь: что работает, а что нет
Когда вы используете кастомный рендеринг (JSON-кампании) или создаете собственные виджеты продуктов (ProductWidgetBuilder
), SDK не может автоматически отслеживать взаимодействия пользователя с вашим UI. В этих случаях вы должны вручную отправлять события, чтобы Gravity Field мог корректно измерять эффективность кампаний.
Существует два основных типа событий взаимодействия:
ContentEngagement
: Относится ко всей кампании или виджету в целом (например, показ баннера).ProductEngagement
: Относится к конкретному продукту внутри виджета (например, клик по карточке товара).
Пример: Ручное отслеживание клика по товару в ProductWidgetBuilder
class CustomProductWidgetBuilder extends ProductWidgetBuilder {
@override
Widget build({
required BuildContext context,
required Slot product,
required CampaignContent content,
required Campaign campaign,
}) {
return GestureDetector(
onTap: () {
// 1. Отправляем событие клика
GravitySDK.instance.sendProductEngagement(
ProductClickEngagement(product, content, campaign),
);
// 2. Выполняем навигацию на страницу товара
// your_navigation_logic(product.item.url);
},
child: Card(
// ... ваша верстка карточки товара
),
);
}
}
Пример: Ручное отслеживание видимости для JSON-кампании
Для отслеживания видимости используйте виджет VisibilityDetector
.
import 'package:visibility_detector/visibility_detector.dart';
// В вашем кастомном виджете, который рендерит JSON-кампанию
@override
Widget build(BuildContext context) {
// ... логика получения campaign и content ...
return VisibilityDetector(
key: Key(content.contentId), // Уникальный ключ для виджета
onVisibilityChanged: (visibilityInfo) {
if (visibilityInfo.visibleFraction >= 0.5) {
// Если виджет виден на 50% или более, отправляем событие
GravitySDK.instance.sendContentEngagement(
ContentVisibleImpressionEngagement(content, campaign),
);
}
},
child: YourCustomCampaignUI(
// ...
),
);
}
#
Справочник по событиям (TriggerEvent)
Этот раздел содержит подробное описание всех классов событий, которые можно отправлять с помощью метода triggerEvent(). Все события наследуются от абстрактного класса TriggerEvent.
#
AddToCartEvent
Событие для отслеживания добавления товара в корзину.
Пример:
final event = AddToCartEvent(
value: 1500.00,
productId: 'sku-abc-1',
quantity: 1,
currency: 'RUB',
cart: [
CartItem(productId: 'sku-abc-1', quantity: 1, itemPrice: 1500.00),
CartItem(productId: 'sku-xyz-3', quantity: 2, itemPrice: 500.00),
],
);
#
PurchaseEvent
Событие для отслеживания успешной покупки.
Пример:
final event = PurchaseEvent(
uniqueTransactionId: 'ORDER-12345',
value: 2550.75,
currency: 'RUB',
cart: [
CartItem(productId: 'sku-123', quantity: 1, itemPrice: 1000.50),
CartItem(productId: 'sku-456', quantity: 2, itemPrice: 775.125),
],
);
#
RemoveFromCartEvent
Событие для отслеживания удаления товара из корзины.
Пример:
final event = RemoveFromCartEvent(
value: 500.00,
productId: 'sku-xyz-3',
quantity: 1,
currency: 'RUB',
cart: [
CartItem(productId: 'sku-abc-1', quantity: 1, itemPrice: 1500.00),
],
);
#
SyncCartEvent
Событие для синхронизации полного состава корзины пользователя.
Пример:
final event = SyncCartEvent(
currency: 'RUB',
cart: [
CartItem(productId: 'sku-abc-1', quantity: 1, itemPrice: 1500.00),
CartItem(productId: 'sku-xyz-3', quantity: 2, itemPrice: 500.00),
],
);
#
AddToWishlistEvent
Событие для отслеживания добавления товара в список желаний.
Пример:
final event = AddToWishlistEvent(
productId: 'sku-fav-789',
);
#
SignUpEvent
Событие для отслеживания регистрации нового пользователя.
Пример:
final event = SignUpEvent(
hashedEmail: 'a1b2c3d4...', // SHA-256 от email
cuid: 'user-555-new',
cuidType: 'internal_user_id',
);
#
LoginEvent
Событие для отслеживания входа пользователя в систему. Ключевое событие для склейки анонимного и авторизованного профилей.
Пример:
final event = LoginEvent(
cuid: 'customer-12345',
cuidType: 'mySystemUserId',
);
#
CustomEvent
Универсальное событие для отслеживания любых других действий, не покрытых стандартными типами.
Пример:
final event = CustomEvent(
type: 'video-watched-v1',
name: 'Просмотр видео',
properties: {
'videoId': 'promo-video-2025',
'watchDuration': '125s',
},
);
#
Обработка обратных вызовов (Callbacks)
SDK предоставляет механизм обратных вызовов (callbacks) для информирования вашего приложения о различных событиях, происходящих внутри. Это критически важно для реализации интерактивных элементов в In-App кампаниях, таких как переходы по ссылкам, диплинкам или запрос разрешений.
Вы можете подписаться на эти события, передав функцию gravityEventCallback при инициализации SDK.
#
Обзор событий (TrackingEvent)
TrackingEvent — это запечатанный (sealed) класс, который объединяет все возможные события. Ниже представлена таблица со всеми типами событий и их описанием.
#
Примеры обработки ключевых событий
Некоторые события, такие как FollowUrlEvent или FollowDeeplinkEvent, требуют обязательной обработки в вашем приложении для обеспечения полной функциональности. SDK лишь сообщает о намерении пользователя, а само действие (открытие ссылки, переход на экран) должно быть реализовано на стороне вашего кода.
#
Обработка FollowUrlEvent
Это событие возникает, когда пользователь нажимает на кнопку или элемент, который должен вести на внешний веб-сайт.
Реализация: Для открытия URL рекомендуется использовать популярный пакет url_launcher.
- Добавьте url_launcher в ваш pubspec.yaml:
dependencies:
url_launcher: ^6.3.1
- Обработайте событие в gravityEventCallback:
import 'package:url_launcher/url_launcher.dart';
// ... в коде инициализации SDK
gravityEventCallback: (event) {
if (event is FollowUrlEvent) {
final uri = Uri.parse(event.url);
// Проверяем, можем ли мы открыть URL
canLaunchUrl(uri).then((canLaunch) {
if (canLaunch) {
// Открываем ссылку во внешнем приложении (браузере)
launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
print('Could not launch ${event.url}');
}
});
}
}
#
Обработка FollowDeeplinkEvent
Это событие сигнализирует, что пользователь нажал на элемент, который должен инициировать навигацию внутри вашего приложения (например, перейти на конкретный экран товара или в раздел "Акции").
Реализация: Логика обработки диплинков полностью зависит от архитектуры навигации в вашем приложении. SDK лишь передает строку диплинка, которую вы должны обработать с помощью вашего роутера (GoRouter, Navigator 2.0, auto_route и т.д.).
Концептуальный пример с GoRouter:
// ... в коде инициализации SDK
gravityEventCallback: (event) {
if (event is FollowDeeplinkEvent) {
// Предполагается, что у вас есть доступ к BuildContext
// или вашему роутеру для выполнения навигации.
// Например, если вы используете GoRouter:
// final router = GoRouter.of(context);
// router.go(event.deeplink); // например, '/product/12345'
print('Navigate to deeplink: ${event.deeplink}');
}
}
💡 Совет: Если у вас нет прямого доступа к BuildContext или роутеру в месте инициализации SDK, вы можете использовать
StreamController
или другой state management подход, чтобы передать событие навигации в UI-слой вашего приложения.
#
Обработка RequestPushEvent
Это событие возникает, когда пользователь выражает желание подписаться на пуш-уведомления, нажав на соответствующий элемент в In-App кампании.
Реализация: В ответ на это событие ваше приложение должно запустить собственную логику запроса разрешений на отправку пуш-уведомлений. SDK не управляет разрешениями напрямую.
Концептуальный пример с firebase_messaging:
import 'package:firebase_messaging/firebase_messaging.dart';
// ... в коде инициализации SDK
gravityEventCallback: (event) {
if (event is RequestPushEvent) {
// Запускаем логику запроса разрешений
requestPushPermissions();
}
}
Future<void> requestPushPermissions() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission();
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
// Можно отправить событие об успешной подписке в вашу аналитику
} else {
print('User declined or has not accepted permission');
}
}