# SDK Gravity Field (Flutter)

Gravity Field SDK — это лёгкий клиент для интеграции мобильных приложений (iOS / Android) с платформой персонализации и A/B-тестирования. Он позволяет запускать кампании без необходимости реализовывать собственную логику таргетинга, выбора и аналитики.

SDK работает по принципу "тонкого клиента": все решения принимает сервер (бекенд Gravity Field), а SDK:

  • передаёт контекст текущего экрана,
  • получает кампании и их содержимое в виде JSON или встроенных шаблонов,
  • активирует показ inline и in-app кампаний
  • трекает взаимодействия пользователя (просмотры, клики, покупки и т.д.),
  • и помогает фиксировать конверсии для аналитики и обучения моделей.

📦 Эта документация предназначена для мобильных разработчиков и инженеров, которые интегрируют SDK в приложение.

Gravity Field SDK

# Добавление библиотеки в проект

  1. Из корня проекта вызовите команду:
flutter pub add gravity_sdk
  1. После добавления плагина в файле pubspec.yaml появится строка с зависимостью:
dependencies:
    gravity_sdk: ^1.0.0
  1. Добавьте импорт:
import 'package:gravity_sdk/gravity_sdk.dart';

# Инициализация SDK

Для работы SDK необходимо провести базовую инициализацию, которая включает регистрацию секции, авторизацию через API-ключ и настройку поведения получения кампаний.

# initialize(...)

Метод принимает обязательные параметры:

void initialize({
  required String section,
  required String apiKey,
  ProductWidgetBuilder? productWidgetBuilder,
  ActionsCallback? actionsCallback,
})

section — ID секции (выдаётся системой)

apiKey — API-ключ (выдаётся системой)

productWidgetBuilder - функция, задающая внешний вид товарной карточки в виджете. Определяется на стороне приложения и используется SDK как шаблон для отображения рекомендаций.

actionsCallback - это глобальный обработчик всех событий взаимодействия с кампаниями и контентом в SDK. Механизм, через который SDK Gravity Field сообщает приложению о событиях, происходящих в кампании.

Полниый список событий, которые могут прилетать в actionsCallback:

Тип события Класс Когда вызывается
Загрузка кампании LoadEvent после trackView / triggerEvent
Отрисовка контента ImpressionEvent когда виджет создан
Попадание в экран VisibleImpressionEvent когда контент стал видимым
Закрытие попапа CloseEvent по нажатию закрыть
Копирование кода CopyEvent при копировании
Отправка формы SubmitEvent при submit формы
Навигация по ссылке FollowUrlEvent клик по URL
Навигация по диплинку FollowDeeplinkEvent переход по deep link
Заявка на пуш-уведомления RequestPushEvent по действию пользователя
Взаимодействие с товарами ProductImpressionEvent показ конкретного товара

Используйте, если вам нужно:

  • Обработка пользовательских действий Например, переход по deeplink FollowDeeplinkEvent или открытие URL FollowUrlEvent.
  • Обработка форм При SubmitEvent приложение получает введённые данные и само решает, что с ними делать: отправить на сервер, показать уведомление, переключить экран и т.д.
  • Отправка событий в аналитику Например: ImpressionEvent, VisibleImpressionEvent, ProductImpressionEvent — удобно логировать показ кампаний и товаров в собственную аналитику (Firebase, AppMetrica, Sentry и т.д.)
  • Встроенные сценарии приложения Использование событий SDK как триггеров для запуска внутренних функций — например, после RequestPushEvent показать системный экран настройки уведомлений.

# Примеры обработки actionsCallback

FollowUrlEvent — открыть ссылку в браузере

import 'package:url_launcher/url_launcher.dart';

// ...
actionsCallback: (event) async {
  if (event is FollowUrlEvent) {
    final uri = Uri.tryParse(event.url);
    if (uri != null && await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    }
  }
}

FollowUrlEvent — открыть ссылку во встроенном WebView

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

// ...
actionsCallback: (event) {
  if (event is FollowUrlEvent) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => Scaffold(
          appBar: AppBar(title: Text('Просмотр')),
          body: WebView(
            initialUrl: event.url,
            javascriptMode: JavascriptMode.unrestricted,
          ),
        ),
      ),
    );
  }
}

Вы можете заменить Scaffold на кастомный экран, если у вас есть свой дизайн для WebView.

FollowDeeplinkEvent — переход по deeplink

import 'package:url_launcher/url_launcher.dart';

// ...
actionsCallback: (event) async {
  if (event is FollowDeeplinkEvent) {
    final uri = Uri.tryParse(event.deeplink);
    if (uri != null && await canLaunchUrl(uri)) {
      await launchUrl(uri);
    }
  }
}

Deeplink может открывать другое приложение или внутренний экран.

RequestPushEvent — запрос разрешения + переход в настройки

import 'package:permission_handler/permission_handler.dart';

// ...
actionsCallback: (event) async {
  if (event is RequestPushEvent) {
    final status = await Permission.notification.status;
    if (!status.isGranted) {
      final result = await Permission.notification.request();
      if (!result.isGranted) {
        // Разрешение не получено — открываем системные настройки
        await openAppSettings();
      }
    }
  }
}

Убедитесь, что в AndroidManifest.xml и Info.plist указаны разрешения на уведомления.

# setOptions(...)

Метод позволяет настроить дополнительные параметры, которые влияют на то, как сервер возвращает кампании и как SDK их обрабатывает.

void setOptions({Options? options, ContentSettings? contentSettings, String? proxyUrl})

class Options {
  final bool isReturnCounter;
  final bool isReturnUserInfo;
  final bool isReturnAnalyticsMetadata;
  final bool isImplicitPageview;
  final bool isImplicitImpression;
  final bool isBuildEngagementUrl = true;

  const Options({
    this.isReturnCounter = false,
    this.isReturnUserInfo = false,
    this.isReturnAnalyticsMetadata = false,
    this.isImplicitPageview = false,
    this.isImplicitImpression = true,
  });
}

class ContentSettings {
  final bool skusOnly;
  final List<String>? fields;

  const ContentSettings({
    this.skusOnly = false,
    this.fields,
  });
}

options

Параметр Тип По умолчанию Описание
isReturnCounter Boolean false Вернуть сработавшие условия и сегменты пользователя
isReturnUserInfo Boolean false Вернуть объект detailedUser с дополнительной информацией о пользователе
isReturnAnalyticsMetadata Boolean false Вернуть аналитику: ID и названия сущностей (кампаний, стратегий, плейсментов)
isImplicitPageview Boolean false Если включено true, SDK автоматически зарегистрирует просмотр страницы при вызове getCampaign(...), без отдельного вызова trackView(...). Это полезно, если вы хотите запрашивать кампании и одновременно зафиксировать pageview.
isImplicitImpression Boolean true Если включено true, SDK сам отправит событие показа контента IMP при получении кампании. Если вы рендерите UI самостоятельно, установите false, чтобы зафиксировать отображение вручную через trackIMP() и т.д.

contentSettings настройки товарного контента

Параметр Тип По умолчанию Описание
maxProducts Int — (на стороне сервера) Максимальное количество возвращаемых товаров (от 1 до 50)
fields List null Явный список полей, которые нужно вернуть для товаров, например: ["name", "price", "image"]
skusOnly Boolean false Вернуть только SKU без описаний, изображений и цен (для экономии трафика)

Таким образом, configureCampaignOptions позволяет заранее определить структуру и поведение всех кампаний, которые будут получены через SDK

proxyUrl

Базовый URL вашего backend-прокси, через который SDK будет отправлять все запросы. Если не задан — используется endpoint Gravity Field.

# Идентификация пользователей

Идентификация пользователя — фундамент для персонализации, аналитики и построения омниканальных профилей в Gravity Field.

Без корректной идентификации невозможно:

  • Связывать действия пользователя между сеансами и устройствами,
  • Персонализировать рекомендации,
  • Точно собирать аналитику,
  • Строить сценарии персонализации и сегментацию.

# Варианты идентификации

Gravity Field позволяет автоматически или вручную управлять идентификацией, гибко подстраиваясь под архитектуру вашего проекта.

Gravity Field UID (автоматическая идентификация)

Когда использовать:

  • Когда нет собственного механизма учёта пользователей и сессий

Как работает:

  • При первом запросе к Gravity Field (например, trackView(...)), если нет сохранённых идентификаторов, Gravity Field автоматически создаёт уникальные идентификаторы:
    • uid — уникальный идентификатор пользователя, автоматически сгенерированный Gravity Field
    • ses — ID сессии, автоматически сгенерированный Gravity Field
  • Эти значения возвращаются в ответе и сохраняются.
  • SDK будет использовать их во всех следующих запросах.
  • Сессия живёт 30 минут и продлевается при активности пользователя.

После авторизации:

  • Чтобы связать анонимный профиль с авторизованным, вызовите событие login через triggerEvent(...).
  • Gravity Field объединит поведение до и после логина.

Custom user id (идентификация через ID приложения)

Когда использовать:

  • Если у пользователя в приложении есть собственный ID и логика управления сессиями.

Как работает:

  • Разработчик вызывает setUser(...).
    • userId — ваш собственный ID пользователя (custom ID);
    • sessionId — ID текущей сессии.
  • SDK использует эти значения в каждом запросе.

💡 Если вы передаёте userId, вы обязаны также передать sessionId. SDK не может создать sessionId автоматически в этом режиме.

# setUser(...)

Позволяет явно установить параметры пользователя и сессии, которые будут автоматически включаться во все запросы SDK к API (/visit, /event, /choose, /engagement).

// Собственный ID пользователя и сессия
void setUser(String userId, String sessionId)

# Как SDK работает с идентификаторами

SDK автоматически управляет идентификаторами пользователя и сессии, опираясь на то, что уже сохранено локально. Используется следующая логика:

  1. Если вызван setUser(...) — SDK использует переданные userId и sessionId (режим: custom user ID).
  2. Если setUser(...) не вызывался, но уже есть uid и ses — SDK использует их (режим: Gravity Field UID).
  3. Если ни один ID не сохранён — SDK отправляет запрос без идентификаторов. Gravity Field создаёт uid и ses, SDK сохраняет их и использует в дальнейшем.

# Передача контекста

Gravity Field принимает решение о показе персонализированного контента на основе контекста страницы, который передаёт SDK.

Контекст страницы позволяет Gravity Field понять:

  • Где сейчас находится пользователь в приложении
  • Какие товары или категории он просматривает
  • В каком регионе или в каких условиях пользователь находится

# trackView(...)

Метод отправляет событие /visit, фиксируя просмотр экрана пользователем. Он формирует тело запроса на основе параметров, переданных клиентом, и автоматически добавленных данных от SDK.

fun trackView(
    ctx: PageContext
)
Поле Тип Описание
ctx Map<String, Any> Контекст текущего экрана (структура соответствует PageContext)

Параметры:

  • isReturnCounter
  • isReturnUserInfo | SDK передает в запросе |

Пример:

sdk.trackView(
    ctx = mapOf(
        "type" to "CART",
        "data" to listOf("cart"),
        "location" to "app://cart",
        "lng" to "msk_warehouse",
        "attributes" to mapOf("segment" to "loyal_users")
    )
)

# PageContext

Для унификации передачи контекста страницы во все методы SDK предлагается использовать отдельную модель:

data class PageContext(
    val type: String, 
    val data: List<String>, 
    val location: String, 
    val lng: String? = null,
    val attributes: Map<String, Any>? = null
)

💡 Типы контекста и соответсвующие для них data описаны здесь: Page context

Поле Тип Описание
type String Тип страницы (HOMEPAGE, PRODUCT, CART, CATEGORY, SEARCH, OTHER)
data List<String> Идентификаторы сущностей (например, SKU, ID категории, ключевое слово поиска)
location String Полный URL или ID текущего экрана в приложении. передается разработчиком приложения
lng String? Код региона или склада, связанный с пользователем или товаром
utm Map<String, String>? UTM-метки маркетинговой кампании
attributes Map<String, Any>? Дополнительные параметры, например сегмент пользователя

# Как активируются кампании на основе контекста страницы

Когда приложение отправляет событие просмотра страницы через trackView(...), SDK передаёт информацию о текущем контексте в Gravity Field. На основе этой информации кампании могут активироваться:

  • Если в Gravity Field настроена кампания, которая должна запускаться на определённый pageView (например, просмотр корзины, карточки товара и т.д.),
  • И условия кампании совпадают с отправленным контекстом страницы (type, data, location и другие параметры),
  • Тогда Gravity Field выберет подходящую кампанию и формирует её содержимое (например, баннер, всплывающее окно или блок рекомендаций), SDK получает готовый контент и отображает в приложение

# Трекинг событий

Трекинг событий позволяет отправлять информацию о действиях пользователя — таких как покупка, добавление товара в корзину, авторизация и другие события. Эти данные используются Gravity Field для аналитики, построения сегментов и запуска кампаний, активируемых по событиям.

Трекинг событий применяется, когда кампании должны срабатывать не по просмотру страницы, а по пользовательскому действию, и также для сбора аналитики.

События фиксируются для того, чтобы измерить поведение пользователя в разных вариациях кампаний: например, узнать, какой из вариантов чекаута приводит к более высокой конверсии. Это позволяет анализировать эффективность персонализации и принимать решения на основе данных.

Платформа поддерживает как системные (предопределённые) события, так и произвольные пользовательские события.

  • Системные события — это события с зафиксированными type, которые платформа распознаёт и может использовать для активации кампаний или анализа (например, purchase, add_to_cart, login).
  • Пользовательские события — разработчик может отправлять любые события с кастомным type. Такие события будут использоваться для аналитики и построения собственных отчётов или логики в Gravity Field.

# triggerEvent(...)

fun triggerEvent(
	events: List<TrackedEvent>,
	ctx: PageContext
)
Параметр Тип Описание
events List<TrackedEvent> Список событий, которые нужно зафиксировать.
ctx PageContext Контекст страницы, на которой произошло событие.

Параметры:

  • isReturnCounter
  • isReturnUserInfo | SDK передает в запросе |

Пример:

sdk.triggerEvent(
    events = listOf(
        TrackedEvent(
            type = "purchase",
            name = "Успешная покупка",
            value = 189.97,
            quantity = 3,
            productId = "sku-123",
            cart = listOf(
                CartItem(
                    productId = "sku-123",
                    quantity = 2,
                    itemPrice = 59.99
                ),
                CartItem(
                    productId = "sku-456",
                    quantity = 1,
                    itemPrice = 69.99
                )
            ),
            customProps = mapOf(
                "promo_code" to "SPRING25",
                "payment_method" to "apple_pay"
            ),
            uniqueTransactionId = "txn-000123456",
            cuid = "external-customer-id-987",
            cuidType = "customer_id",
            hashedEmail = "c02b82b72772339fa47f1b1f2940f78f20e4a4b6d98ea9d3c244f7c8e637eabc"
        )
    ),
    ctx = PageContext(
        type = "CHECKOUT",
        data = listOf("checkout"),
        location = "app://checkout"
    )
)

# TrackedEvent

Соответствие типов эвентов и параметров

Поле Тип Описание
type string Системное имя события (например, "purchase", "add_to_cart", "login").
name string Человеко-читаемое название события. Используется для аналитики.
currency string Валюта события (например, "USD"). Если не указано — используется валюта секции.
value number Суммарная стоимость добавленных товаров.
quantity integer Количество товаров, участвовавших в событии.
productId string SKU товара, если событие связано с конкретным продуктом.
cart array Массив объектов CartItem, представляющий содержимое корзины.
customProps object Кастомные поля события.
uniqueTransactionId string Уникальный ID события. Защищает от повторной отправки (например, при покупке).
cuid string Внешний идентификатор пользователя (например, customer_id).
cuidType string Тип поля cuid.
hashedEmail string SHA256-хеш email пользователя.

# CartItem

Поле Тип Описание
productId string Идентификатор товара (SKU)
quantity integer Количество единиц товара
itemPrice number Цена за единицу товара

# Как активируются кампании по событию

Когда приложение отправляет событие через triggerEvent(...), SDK передаёт информацию о событии и текущем контексте в Gravity Field. На основе этой информации кампании могут активироваться:

  • Если в Gravity Field настроена кампания, которая должна запускаться на определённый trackedEvent (например, добавление в корзину, покупку и т.д.),
  • И условия кампании совпадают с отправленным контекстом страницы (type, data, location и другие параметры),
  • Тогда Gravity Field выберет подходящую кампанию и формирует её содержимое (например, баннер, всплывающее окно или блок рекомендаций), SDK получает готовый контент и отображает в приложение

# Активация кампаний

# Inline-блоки (встраиваемые кампании в UI)

Кампания встраивается в существующую структуру экрана приложения — но только если условия показа, заданные на платформе Gravity Field, выполнены.

GravityInlineWidget

SDK запрашивает кампанию по selector, передаёт контекст текущей страницы, и если сервер вернёт релевантный контент — он будет отображён в заданном UI-элементе.

GravityInlineWidget({
  required String selector,
  String? placeholderId,
  double? width,
  double? height,
  Map<String, dynamic>? attributes,
})
Параметр Тип Описание
selector String Уникальный идентификатор кампании, используемый для запроса контента
width double? Ширина блока. По умолчанию занимает всё доступное пространство?
height double? Высота блока. Может быть зафиксирована вручную.
attributes Map<String, dynamic>? Дополнительные параметры, которые передаются вместе с запросом кампании
placeholderId String ID места в интерфейсе, куда вставляется виджет, в мультивиджет кампании

selector задается в настройках кампании, в личном кабинете Gravity Field.

placeholderId задается в настройках вариации кампании, в лично кабинете Gravity Field

💡 Если условия показа не выполнены (например, пользователь не входит в целевую группу), блок не будет отрисован.

📌 Подходит для: баннеров, товарных рекомендаций, УТП блоков.

Пример:

class InlineScreen extends StatelessWidget {
  const InlineScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Inline Screen'),
      ),
      body: Column(
        children: [
          GravityInlineWidget(
            width: 200,
            height: 200,
            selector: 'homepage-rec-widget'
          ),
          SizedBox(height: 36,),
          GravityInlineWidget(
            selector: 'homepage-banner',
          )
        ],
      ),
    );
  }
}

# [WIP]Кастомизация внешнего вида товарных рекомендаций

TODO

# In-app кампании (всплывающие окна, модальные экраны)

Кампания отображается поверх интерфейса — это может быть snackbar, bottom sheet, или модальное окно. Такие кампании активируются автоматически, если условия показа, настроенные в платформе Gravity Field, выполнены.

Когда и как происходит активация:

  • SDK отправляет запрос через trackView(...) или triggerEvent(...) с текущим контекстом страницы и события
  • Сервер Gravity Field анализирует условия:
    • Целевая страница (ctx.data)
    • Условия сегментации (аудитория, сегменты, сессия, гео и т.д.)
    • Частота показов, лимиты, включённость в эксперимент
  • Если кампания активна и все условия выполнены — сервер возвращает кампанию
  • SDK автоматически отображает in-app сообщение через встроенные шаблоны

📌 Подходит для:

  • Всплывающих предложений (например, «вернитесь в корзину»)
  • Промо после события (purchase, login, logout)
  • Модальных сообщений при входе на экран
  • A/B-тестов с воронками входа / выхода

# Отображение JSON-кампаний на стороне приложения

В отличие от in-app и inline кампаний, которые SDK может отрисовать автоматически (например, баннеры, модалки, ленты товаров по шаблонам), JSON-кампании не имеют визуального шаблона. Gravity Field возвращает только структурированные данные, и отображение полностью реализуется на стороне приложения.

Есть два способа получить кампанию:

Вариант 1: Через getCampaign(...)

Метод getCampaign(...) используется, когда кампания нужна по конкретному selector или groupSelector.

fun getCampaign(
    selector: String? = null,
    groupSelector: String? = null,
    page: PageContext? = null,
    onResult: (campaigns: List<Campaign>) -> Unit
)
Параметр Тип Описание
selector String? Уникальный идентификатор кампании (например, "checkout_banner")
groupSelector String? Групповой селектор, объединяющий несколько кампаний
page PageContext? Контекст страницы, используется для проверки условий показа
onResult (List<Campaign>) -> Unit Колбэк с результатом запроса кампаний

📌 Подходит для:

  • Получения кампаний заранее (например, до отрисовки экрана)
  • Встраивания кампаний в кастомные блоки

Вариант 2: Через actionsCallback после события или просмотра экрана

Если вам нужно вручную обработать кампанию (например, отрисовать попап или вставить блок) — используйте параметр actionsCallback при инициализации SDK.

Событие с типом Load приходит сразу после отправки события (triggerEvent(...)) или просмотра страницы (trackView(...)). Вы можете проверить selector и решить, нужно ли отображать кампанию.

GravityFieldSdk.initialize(
  sectionId: 'abc123',
  apiKey: 'gf_xyz...',
  actionsCallback: (event) {
    if (event.type == 'LoadEvent' && event.selector == 'custom_popup') {
      // кастомная логика показа кампании
      showCustomPopup(event.campaign);
    }
  },
);

📌 Такой подход позволяет:

  • Получать кампании в момент события или просмотра экрана
  • Гибко управлять отображением кампаний вручную
  • Тестировать варианты крупных блоков в приложении, например, новый дизайн карточки товара

# Campaign

Объект Campaign представляет собой активированную персонализированную кампанию, полученную от сервера Gravity Field через getCampaign(...), trackView(...) или triggerEvent(...)

data class Campaign(
    val selector: String,               
    val groupSelector: String?,         
    val decisionId: String,             
    val contents: List<CampaignContent> 
)
Поле Тип Описание
selector String Уникальный селектор кампании
groupSelector String? Общий селектор группы кампаний, например несколько виджетов на одной странице
decisionId String Уникальный ID назначения кампании этому пользователю
contents List<CampaignContent> Массив контентных элементов кампании (один или несколько)

Почему contents — это список

Одна кампания может содержать несколько визуальных элементов, например:

  • Баннер + товарная лента
  • Модальное окно + напоминание
  • Несколько последовательных шагов (например, туториал)

Каждый элемент описывается через CampaignContent.

# CampaignContent

data class CampaignContent(
    val contentId: String,
    val deliveryMethod: String,
    val placeholderId: String,
    val step: Integer,
    val templateId: String,
    val templateSystemName: String,
    val contentType: String,
    val custom: JSONObject,
    val products: ProductsBlock?
)
Поле Тип Описание
contentId String Уникальный ID элемента внутри кампании
contentType String Тип контента: products, json
custom JSONObject JSON с UI-контентом: текст, стили, кнопки, действия
products JSONObject? Опциональный блок с товарами
deliveryMethod String Способ доставки контента: bottom-sheet, snackbar, и тд
placeholderId String ID места в интерфейсе, куда вставляется виджет, в мультивиджет кампании
step Integer Номер шага в многошаговой кампании (например, 1 — snackbar, 2 — overlay)
templateId String Идентификатор шаблона
templateSystemName String Системное имя шаблона

# ProductsBlock

data class ProductsBlock(
    val strategyId: String,
    val name: String?,
    val pageNumber: Int?,
    val countPages: Int?,
    val fallback: Boolean,
    val slots: List<ProductSlot>
)
Поле Тип Описание
strategyId String ID стратегии рекомендаций, сгенерированной на сервере
name String? Название стратегии (например, "Похожие товары")
pageNumber Int? Номер страницы в пагинации
countPages Int? Общее количество страниц, если товары разбиты на страницы
fallback Boolean Признак использования резервного алгоритма
slots List<ProductSlot> Список товарных позиций

# ProductSlot

data class ProductSlot(
    val item: JSONObject,           
    val fallback: Boolean,
    val strId: Int,
    val slotId: String,
)
Поле Тип Описание
item JSONObject Полная информация о товаре (sku, цена, имя и т.д.)
fallback Boolean Был ли этот товар вставлен как fallback (если стратегия ничего не вернула)
strId Int Порядковый ID в стратегии (может использоваться для сортировки)
slotId String Уникальный ID товарного слота, используется для трекинга (PIMP)

# Трекинг взаимодействий (engagement) с кампанией

Engagement-события для кампаний позволяют Gravity Field анализировать поведение пользователя в деталях:

  • Какой контент был показан и как он повлиял на поведение
  • Какая вариация кампании эффективнее: какой баннер чаще просматривают, на какой товар чаще кликают, что влияет на покупку
  • Понимание того, какие элементы UI реально видны (WRIMP), позволяет фильтровать «слепые зоны» и улучшать UX
  • Фиксация кликов и показов даёт системе обратную связь: что работает, а что нет

in-app и inline(баннерных) кампании — SDK автоматически отслеживает показ, клики и видимость блоков.

json кампании — SDK не знает, когда блок был показан, видим или кликнут. Поэтому вы должны вручную вызывать трекинг нужных событий.

inline(товарные) кампании — SDK автоматически отслеживает показ, так как карточки отрисовывает приложение через productWidgetBuilder Поэтому вы должны вручную вызывать трекинг кликов(PCLICK) по товарам.

SDK отдаёт готовые методы для их трекинга — вам не нужно вручную формировать запросы.

CampaignContent содержит методы, по которым Gravity Field отслеживает взаимодействие пользователя:

Название Тип Описание
trackLoad load Контент получен — готов к показу
trackIMP IMP Блок отрисован
trackWRIMP WRIMP Блок виден >50% более 1 секунды
trackWCLICK WCLICK Пользователь кликнул по блоку

ProductSlot содержит методы, по которым Gravity Field отслеживает взаимодействие пользователя c товаром:

Событие Тип Описание
trackPIMP PIMP Показ товара (например, в рекомендательной ленте)
trackPCLICK PCLICK Клик по товару (например, открыть карточку товара)

🔗 Все эти события привязаны к decisionId кампании и, при необходимости, к contentId и slotId товара.

Пример: отображение и трекинг УТП-блока

Шаг 1: Получение кампании

sdk.getCampaign(
    selector = "checkout_usp_banner",
    page = PageContext(
        type = "CART",
        data = listOf("cart"),
        location = "app://cart"
    ),
    onResult = { campaigns ->
        campaigns.firstOrNull()?.let { campaign ->
            val content = campaign.contents.firstOrNull()
            if (content != null) {
                renderUspBlock(content)
            }
        }
    }
)

Пример JSON-контента кампании

{
  "title": "Бесплатная доставка сегодня!",
  "text": "Оформите заказ до 23:59 и получите бесплатную доставку.",
  "button": {
    "text": "Оформить заказ",
    "action": "navigate",
    "target": "app://checkout"
  }
}

Шаг 2: Рендер блока

fun renderUspBlock(content: CampaignContent) {
    val json = content.custom

    val title = json.optString("title")
    val text = json.optString("text")
    val buttonText = json.optJSONObject("button")?.optString("text")
    val target = json.optJSONObject("button")?.optString("target")

    uspTitleView.text = title
    uspDescriptionView.text = text
    uspButton.text = buttonText

    // Трекинг показа
    content.trackIMP()

    // Отложенный трекинг видимости (> 1с на экране)
    if (isVisibleForMoreThan1Second(uspBlock)) {
        content.trackWRIMP()
    }

    // Обработка нажатия
    uspButton.setOnClickListener {
        content.trackWCLICK()
        navigateTo(target)
    }
}

Что в итоге делает разработчик:

  • Получает кампанию по selector
  • Отрисовывает JSON как UI
  • Вызывает engagement-трекинг вручную (trackIMP, trackWRIMP, trackWCLICK)

# Все события для колбека

abstract class TrackingEvent {
  final Campaign campaign;

  const TrackingEvent({required this.campaign});
}

// Общие события

class LoadEvent extends TrackingEvent {

  const LoadEvent({
    required super.campaign,
  });
}

class ImpressionEvent extends TrackingEvent {
  final Content content;

  const ImpressionEvent({
    required super.campaign,
    required this.content,
  });
}

class VisibleImpressionEvent extends TrackingEvent {
  final Content content;

  const VisibleImpressionEvent({
    required super.campaign,
    required this.content,
  });
}

class CloseEvent extends TrackingEvent {
  final Content content;
  final String source;

  const CloseEvent({
    required super.campaign,
    required this.content,
    required this.source,
  });
}

class CopyEvent extends TrackingEvent {
  final Content content;
  final String copiedValue;

  const CopyEvent({
    required super.campaign,
    required this.content,
    required this.copiedValue,
  });
}

class CancelEvent extends TrackingEvent {
  final Content content;

  const CancelEvent({
    required super.campaign,
    required this.content,
  });
}

class SubmitEvent extends TrackingEvent {
  final Content content;
  final Map<String, dynamic> formData;

  const SubmitEvent({
    required super.campaign,
    required this.content,
    required this.formData,
  });
}

class NextStepEvent extends TrackingEvent {
  final Content content;
  final int step;

  const NextStepEvent({
    required super.campaign,
    required this.content,
    required this.step,
  });
}

class FollowUrlEvent extends TrackingEvent {
  final Content content;
  final Uri url;

  const FollowUrlEvent({
    required super.campaign,
    required this.content,
    required this.url,
  });
}

class FollowDeeplinkEvent extends TrackingEvent {
  final Content content;
  final Uri deeplink;

  const FollowDeeplinkEvent({
    required super.campaign,
    required this.content,
    required this.deeplink,
  });
}

class RequestPushEvent extends TrackingEvent {
  final Content content;

  const RequestPushEvent({
    required super.campaign,
    required this.content,
  });
}

class RequestTrackingEvent extends TrackingEvent {
  final Content content;

  const RequestTrackingEvent({
    required super.campaign,
    required this.content,
  });
}

// товарные

class ProductImpressionEvent extends TrackingEvent {
  final Slot slot;
  final Content content;

  const ProductImpressionEvent({
    required super.campaign,
    required this.slot,
    required this.content,
  });
}