#
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.10.2 # Замените на актуальную версию
- Добавьте импорт:
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 для обработки навигации и других действий.
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) {
// ...
}
}
#
Почему важен gravityEventCallback?
SDK не выполняет навигацию самостоятельно. Когда пользователь нажимает на кнопку в кампании, SDK генерирует событие (например, FollowUrlEvent) и передает его в gravityEventCallback. Ваше приложение должно «поймать» это событие и выполнить соответствующее действие (открыть ссылку, перейти на другой экран). Без этого интерактивные элементы работать не будут.
Для открытия URL мы использовали пакет url_launcher. Не забудьте добавить его в ваш pubspec.yaml:
dependencies:
url_launcher: ^6.3.1
Подробнее о событиях см. раздел triggerEvent с CustomEvent.
#
Шаг 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 автоматически покажет ее.
#
Почему SDK требует BuildContext?
SDK использует BuildContext для доступа к дереву виджетов и ThemeData вашего приложения. Это позволяет:
- Найти
ScaffoldMessengerдля показаSnackBar. - Использовать
Navigatorдля отображения диалогов, шторок и полноэкранных кампаний. - Наследовать стили (шрифты, цвета) из глобальной темы, чтобы кампании выглядели нативно.
#
Шаг 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, // Обязательно задайте высоту для блока
pageContext: PageContext(
type: ContextType.homepage,
data: [],
location: 'app://homepage',
),
),
// ... другие виджеты
],
),
);
}
Виджет сам загрузит и отобразит релевантный контент по селектору homepage-recommendations.
#
Инициализация и конфигурация
#
initialize()
Основной метод для настройки SDK. Вызывается один раз при старте приложения.
Future<void> initialize({
required String apiKey,
required String section,
ProductWidgetBuilder? productWidgetBuilder,
GravityEventCallback? gravityEventCallback,
LogLevel logLevel = LogLevel.info,
});
apiKey(required): Ваш уникальный ключ API.section(required): Идентификатор секции вашего проекта.productWidgetBuilder: Опциональный билдер для кастомизации виджетов продуктов. (См. раздел8. Кастомизация UI ).gravityEventCallback: Опциональный колбэк для получения уведомлений о событиях SDK. (См. раздел11. Обработка обратных вызовов (Callbacks) ).logLevel: Уровень детализации логов SDK. По умолчаниюLogLevel.info. (См. раздел5.3. Настройка логирования ).
#
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'], // Запросить конкретные поля
),
);
#
Настройка логирования
При инициализации SDK вы можете указать уровень детализации логов с помощью параметра logLevel. Это полезно для отладки интеграции.
await GravitySDK.instance.initialize(
apiKey: 'YOUR_API_KEY',
section: 'YOUR_SECTION_ID',
logLevel: LogLevel.debug, // Устанавливаем максимальный уровень детализации
);
Доступные уровни LogLevel:
- LogLevel.none: Логирование полностью отключено.
- LogLevel.error: Только ошибки.
- LogLevel.warn: Ошибки и предупреждения.
- LogLevel.info: (По умолчанию) Информационные сообщения, ошибки и предупреждения.
- LogLevel.debug: Максимальная детализация, включая тела запросов и ответов.
#
Идентификация пользователя
SDK поддерживает два подхода к идентификации: автоматический (управляется SDK) и ручной (управляется вашим приложением).
#
Автоматическая идентификация (SDK-managed)
Это подход по умолчанию. При первом запросе SDK получает от сервера уникальный uid (user ID) и ses (session ID) и сохраняет их на устройстве. Все последующие запросы будут использовать эти идентификаторы.
Чтобы связать анонимный профиль с профилем авторизованного пользователя, после входа в систему отправьте 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',
),
);
}
#
Ручная идентификация
Если ваше приложение уже управляет ID пользователей и сессий, вы можете передавать их в SDK напрямую с помощью метода setUser.
#
setUser()
Устанавливает custom (ваш ID пользователя) и ses (ваш ID сессии) для всех последующих запросов.
void setUser(String userId, String sessionId);
Пример:
// Вызывается при старте сессии, если ID уже известны
GravitySDK.instance.setUser('user-from-my-system-42', 'session-from-my-system-xyz');
В этом режиме SDK не будет использовать автоматически сгенерированный uid.
#
Передача контекста
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
Ключевая модель для описания местоположения и контекста пользователя.
💡 Типы контекста и соответсвующие для них data описаны здесь: Page context
#
Трекинг событий
Трекинг событий позволяет отправлять информацию о действиях пользователя — таких как покупка, добавление товара в корзину, авторизация и другие события. Эти данные используются Gravity Field для аналитики, построения сегментов и запуска кампаний, активируемых по событиям.
Трекинг событий применяется, когда кампании должны срабатывать не по просмотру страницы, а по пользовательскому действию, и также для сбора аналитики.
События фиксируются для того, чтобы измерить поведение пользователя в разных вариациях кампаний: например, узнать, какой из вариантов чекаута приводит к более высокой конверсии. Это позволяет анализировать эффективность персонализации и принимать решения на основе данных.
Платформа поддерживает как системные (предопределённые) события, так и произвольные пользовательские события.
- Системные события — это события с зафиксированными
type, которые платформа распознаёт и может использовать для активации кампаний или анализа (например,purchase,add_to_cart,login). - Пользовательские события — разработчик может отправлять любые события с кастомным
type. Такие события будут использоваться для аналитики и построения собственных отчётов или логики в Gravity Field.
#
triggerEvent(...)
Отправляет одно или несколько событий о действиях пользователя.
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',
),
);
Подробное описание всех доступных событий и их параметров — в разделе ниже.
#
Передача статуса Push-уведомлений
Чтобы платформа могла таргетировать кампании на пользователей в зависимости от статуса подписки на push-уведомления, необходимо передавать этот статус в SDK.
#
setNotificationPermissionStatus()
Устанавливает текущий статус разрешения на push-уведомления.
void setNotificationPermissionStatus(NotificationPermissionStatus status);
• status: NotificationPermissionStatus.granted, NotificationPermissionStatus.denied или NotificationPermissionStatus.unknown.
Пример получения и установки статуса (через firebase_messaging):
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:gravity_sdk/gravity_sdk.dart';
// 1. Получаем статус из системы
Future<NotificationPermissionStatus> getNotificationStatus() async {
final settings = await FirebaseMessaging.instance.getNotificationSettings();
switch (settings.authorizationStatus) {
case AuthorizationStatus.authorized:
case AuthorizationStatus.provisional:
return NotificationPermissionStatus.granted;
case AuthorizationStatus.denied:
return NotificationPermissionStatus.denied;
default:
return NotificationPermissionStatus.unknown;
}
}
// 2. Передаем статус в SDK при старте приложения и после запроса разрешений
final status = await getNotificationStatus();
GravitySDK.instance.setNotificationPermissionStatus(status);
#
FAQ / Troubleshooting
Почему
GravityInlineWidgetне отображается или имеет нулевую высоту?- Вы не задали обязательный параметр
height. Задайте высоту в конструктореGravityInlineWidget.
- Вы не задали обязательный параметр
Почему UI кампании выглядит иначе, чем в превью?
- SDK наследует стили из
ThemeDataприложения. Если в кампании не задан шрифт/цвет, используетсяTheme.of(context).
- SDK наследует стили из
Почему клик по кнопке в кампании не приводит к переходу?
- Не обработаны события
FollowUrlEvent/FollowDeeplinkEventвgravityEventCallback. SDK не выполняет навигацию сам.
- Не обработаны события
#
Работа с контентом
#
GravityInlineWidget (Автоматический рендеринг)
Виджет для встраивания кампаний (например, товарных рекомендаций) прямо в верстку вашего экрана.
Для отображения нескольких виджетов из одной кампании (мультивиджет-кампании) используйте параметр placeholderId:
// Пример для мультивиджет кампании
Column(
children: [
GravityInlineWidget(
selector: 'homepage-recs',
placeholderId: 'placeholder-1',
height: 250,
pageContext: PageContext(
type: ContextType.homepage,
data: [],
location: 'app://homepage',
),
),
GravityInlineWidget(
selector: 'homepage-recs',
placeholderId: 'placeholder-2',
height: 250,
pageContext: PageContext(
type: ContextType.homepage,
data: [],
location: 'app://homepage',
),
),
],
)
#
Параметры виджета
#
ProductWidgetBuilder (Кастомный рендеринг товаров)
Для полного контроля над внешним видом карточек товаров в GravityInlineWidget, создайте свой класс, наследующий ProductWidgetBuilder.
// 1. Создайте свой билдер
class CustomProductWidgetBuilder extends ProductWidgetBuilder {
@override
Widget build({
required BuildContext context,
required Slot product,
required CampaignContent content,
required Campaign campaign,
}) {
// Ваша кастомная верстка карточки товара
return GestureDetector(
onTap: () {
// При клике важно отправлять событие взаимодействия
GravitySDK.instance.sendProductEngagement(
ProductClickEngagement(product, content, campaign)
);
// ... и выполнять переход на экран товара
},
child: Card(
child: Text(product.item['name'] as String? ?? ''),
),
);
}
}
// 2. Передайте его в initialize
await GravitySDK.instance.initialize(
// ...
productWidgetBuilder: CustomProductWidgetBuilder(),
);
⚠️ Обратите внимание:
ProductWidgetBuilderотвечает за отрисовку одной карточки товара. Эта карточка будет автоматически помещена SDK в соответствующий контейнер:ListViewдля горизонтального скролла (row) илиGridViewдля отображения сеткой (grid), в зависимости от настроек кампании.
#
10.2. Отображение товарных рекомендаций (products-container)
Если кампания возвращает список товаров (например, товарные рекомендации), SDK автоматически отрисовывает их внутри специального элемента products-container. Этот контейнер управляет расположением товарных карточек и может работать в двух режимах:
row(горизонтальный список): Товары отображаются в виде горизонтального прокручиваемого списка (ListView). Это поведение по умолчанию.grid(сетка): Товары отображаются в виде сетки (GridView). Количество колонок и отступы между элементами настраиваются в шаблоне кампании.
Тип контейнера (row или grid) и его стили задаются в шаблоне кампании в интерфейсе Gravity Field. SDK автоматически применяет эти настройки при рендеринге.
Роль ProductWidgetBuilder в этом процессе — отрисовать одну карточку товара, которая затем будет помещена в соответствующий контейнер. Таким образом, вы контролируете внешний вид отдельного товара, а Gravity Field — их общее расположение.
⚠️ Обратите внимание: product.item представляет собой Map<String, dynamic>. Ключи в этой карте (например, 'name', 'price', 'imageUrl') соответствуют полям в вашем товарном фиде. Убедитесь, что вы используете правильные ключи и безопасно обрабатываете типы данных и возможные null значения.
#
JSON-кампании (Полностью ручной рендеринг)
Для максимальной гибкости вы можете получать кампании в виде чистого JSON и рендерить их самостоятельно.
⚠️ Важно: При работе с JSON-кампаниями SDK лишь доставляет данные. Ваше приложение несет полную ответственность за парсер JSON-объекта, полученного из
response.data, и последующий рендеринг UI на основе этих данных.
#
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) {
// Здесь ваша логика обработки и отображения данных из event.content
}
},
);
#
Трекинг взаимодействий (engagement)
Трекинг взаимодействий (engagement) в Flutter SDK организован на двух уровнях, чтобы обеспечить гибкий сбор аналитики как по виджетам в целом, так и по отдельным товарам внутри них. Понимание этой иерархии — ключ к правильной интеграции и достоверным отчетам.
#
Иерархия событий в кампании
Чтобы понять, какое событие за что отвечает, важно видеть их место в структуре данных кампании.
graph TD
subgraph Структура Кампании
A[Кампания / Виджет] --> B(content.events)
B --> C["WRIMP (Показ всего виджета)"]
B --> D["WCLICK (Клик по виджету/контенту)"]
A --> E[products]
E -- содержит --> F{products.slots}
subgraph Уровень Товара
F --> G{Slot 1}
F --> H{Slot 2}
F --> I[...]
G --> J(products.slots.events)
J --> K["PIMP (Показ товара)"]
J --> L["PCLICK (Клик по товару)"]
end
end
#
Диаграмма последовательности событий взаимодействия
sequenceDiagram
participant User as Пользователь
participant App as Приложение (UI)
participant SDK as Gravity SDK
participant Server as Gravity Field
User->>App: Скролл до виджета / экран показан
App->>SDK: Виджет стал видимым (>=50%)
SDK->>Server: WRIMP (ContentVisibleImpression)
SDK->>Server: PIMP для каждого видимого товара
User->>App: Клик по товару
App->>SDK: sendProductEngagement(ProductClickEngagement)
SDK->>Server: PCLICK
opt Дополнительно для A/B (опционально)
App->>SDK: sendContentEngagement(ContentClickEngagement)
SDK->>Server: WCLICK
end
Server-->>SDK: 200 OK
#
Назначение и использование событий
Каждый тип события служит для определенных аналитических целей.
#
События уровня виджета (WRIMP и WCLICK)
Эти события отслеживают взаимодействие со всей кампанией (виджетом).
- WRIMP (Widget Real Impression): Фиксирует показ всего виджета, когда он попадает в зону видимости пользователя. Это базовое событие для отслеживания видимости кампании.
- WCLICK (Widget Click): Используется для отслеживания кликов по не-рекомендательным виджетам, таким как баннеры, in-app, кнопки и тд.
#
События уровня товара (PIMP и PCLICK)
Эти события необходимы для сбора статистики по рекомендательным кампаниям.
- PIMP (Product Impression): Фиксирует показ отдельного товара в рекомендательном блоке, когда он становится видимым.
- PCLICK (Product Click): Ключевое событие для аналитики рекомендаций. Оно содержит информацию о конкретном товаре и рекомендательной стратегии. Отправка
PCLICKобязательна для того, чтобы в отчете по рекомендательным стратегиям появилась аналитика по кликам на товары.
💡 Продвинутый сценарий: WCLICK для A/B-тестов рекомендаций
В случае рекомендательных кампаний, разработчик может опционально отправлять событие WCLICK при клике на любую область виджета (включая клик по товару, одновременно с
PCLICK).Зачем это нужно? Это полезно для A/B-тестирования, когда требуется сравнить общий CTR (Click-Through Rate) разных рекомендательных алгоритмов.
WCLICKпозволяет измерить кликабельность всего виджета как единого целого, в то время какPCLICKизмеряет интерес к конкретным товарам.
#
Функции SDK для ручной отправки событий
Когда SDK не может автоматически отследить взаимодействие (например, в кастомном UI), вы должны отправлять события вручную. Для этого в SDK предусмотрены два метода:
GravitySDK.instance.sendContentEngagement()
Используется для отслеживания взаимодействий с виджетом в целом (события WRIMP/WCLICK). По умолчанию SDK автоматически отправляет эти события для встроенных форматов. Это актуально когда вы получаете кампанию через getContentBySelector(...), где вы сами отвечаете за рендеринг.
GravitySDK.instance.sendProductEngagement()
Используется для отслеживания взаимодействий с отдельными товарами внутри виджета (события PIMP/PCLICK). Отправка PCLICK обязательна при использовании ProductWidgetBuilder. PIMP отправляется автоматически.
#
Сценарий: Клик по товару в кастомном ProductWidgetBuilder
Если вы реализуете свой ProductWidgetBuilder, вы обязаны вручную отправлять событие PCLICK при нажатии на товар. SDK автоматически отслеживает только показ (PIMP) кастомных виджетов.
import 'package:gravity_sdk/gravity_sdk.dart';
class CustomProductWidgetBuilder extends ProductWidgetBuilder {
@override
Widget build({
required BuildContext context,
required Slot product,
required CampaignContent content,
required Campaign campaign,
}) {
return GestureDetector(
onTap: () {
// 1. Отправляем событие клика по товару (PCLICK)
GravitySDK.instance.sendProductEngagement(
ProductClickEngagement(product, content, campaign),
);
// 2. Выполняем навигацию на страницу товара
// your_navigation_logic(product.item['url']);
},
child: Card(
// ... ваша верстка карточки товара
),
);
}
}
#
Справочник по событиям (TriggerEvent)
В таблице ниже описаны основные классы событий, которые можно отправлять с помощью triggerEvent(...).
#
Обработка обратных вызовов (Callbacks)
Подпишитесь на события SDK, передав функцию gravityEventCallback в initialize. Это критично для обработки навигации и запросов разрешений.
#
Справочник по событиям (TrackingEvent)
Пример обработки:
gravityEventCallback: (event) {
if (event is FollowUrlEvent) {
// Открыть URL с помощью url_launcher
} else if (event is FollowDeeplinkEvent) {
// Выполнить навигацию с помощью вашего роутера
} else if (event is RequestPushEvent) {
// Запросить разрешение на push-уведомления
}
}
Чтобы видеть клики/навигацию в отчетах, можно дополнительно отправлять CustomEvent через triggerEvent(...) в обработчиках колбэка.
#
FAQ (Часто задаваемые вопросы)
Q: В чем разница между trackView и triggerEvent?
A: Оба метода могут инициировать показ кампании. Используйте trackView для отслеживания просмотров экранов (например, главная, карточка товара, корзина). Используйте triggerEvent для отслеживания конкретных действий пользователя (например, добавление в корзину, покупка, логин).
Q: Почему мой GravityInlineWidget не отображается или имеет нулевую высоту?
A: Самая частая причина — для GravityInlineWidget не задан параметр height. Flutter требует явных ограничений по размеру для многих виджетов. Убедитесь, что вы передали конкретное значение высоты: height: 250.
Q: Как обрабатывать клики по кнопкам и ссылкам в In-App сообщениях (например, в модальном окне)?
A: SDK отправляет события FollowUrlEvent (для внешних ссылок) и FollowDeeplinkEvent (для внутренней навигации) в колбэк gravityEventCallback, который вы передаете при инициализации. Вы должны реализовать логику обработки этих событий, например, используя пакет url_launcher для открытия ссылок. SDK сам навигацию не выполняет.
Q: Что произойдет, если SDK не сможет получить кампанию от сервера (например, нет интернета)?
A: SDK обработает ошибку внутри. Если это GravityInlineWidget, он просто не будет отображен (останется пустым). Если это In-App кампания, она не будет показана. Ошибки не должны приводить к падению приложения.
Q: Почему в ProductWidgetBuilder я должен сам отправлять событие клика?
A: Потому что вы создаете полностью кастомный виджет. SDK не знает его внутреннюю структуру и на какой именно элемент пользователь должен нажать. SDK автоматически отслеживает только видимость вашей карточки, а за клик отвечает ваш код через вызов sendProductEngagement().
Q: Как мне протестировать кампанию перед запуском на всех пользователей?
A: Используйте таргетинг по CUID (Custom User ID) в настройках кампании в интерфейсе Gravity Field. Укажите свой тестовый ID, и кампания будет показываться только вам.