# Контекст экранов и события

Импорт для примеров: `package:gravity_sdk/gravity_sdk.dart`. Методы с автопоказом дополнительно используют `BuildContext` из Flutter.

## PageContext

```dart
const productContext = PageContext(
  type: ContextType.product,
  data: ['sku-123'],
  location: 'app://product/sku-123',
  attributes: {'screen_variant': 'standard'},
);
```

| Поле | Тип | Как передавать |
|---|---|---|
| `type` | `ContextType` | Обязательно; тип текущего экрана |
| `data` | `List<String>` | Обязательно; значения для типа контекста, например SKU товара; `[]`, если неприменимо |
| `location` | `String` | Обязательно; адрес/идентификатор экрана, согласованный с таргетингом |
| `lng` | `String?` | Региональное значение из фида, если проект его использует |
| `pageNumber` | `int?` | Номер страницы выдачи |
| `referrer` | `String?` | Предыдущий адрес |
| `utm` | `Map<String, String>?` | Метки источника |
| `attributes` | `Map<String, Object>` | Дополнительные атрибуты контекста; по умолчанию `{}` |

Типы: `homepage`, `product`, `cart`, `category`, `search`, `other`. SDK сериализует их как `HOMEPAGE`, `PRODUCT`, `CART`, `CATEGORY`, `SEARCH` и `OTHER`.

`data` задаёт контекст выбора/таргетинга и не заменяет состав корзины в событии. Общие правила для каждого типа: [Контекст страницы](/Integration/web_integration/page_context.md). Не меняйте регистр SKU, категорий и `lng` относительно товарного фида.

SDK добавляет в `attributes` служебные `app_version`, `sdk_version` и `app_platform`. Для iOS значение платформы — `iOS`. [Таргетинг по платформе и версиям](/personalization/Campaigns/targeting_schedule.md#таргетинг-sdk-кампаний-по-платформе-и-версиям).

## Просмотр экрана

`trackView()` передаёт просмотр и может автоматически показать in-app кампанию:

```dart
await GravitySDK.instance.trackView(
  context: context,
  pageContext: productContext,
);
```

Вызывайте после появления экрана, например через `addPostFrameCallback`, как в [быстром старте](./ask_flutter.md). Не отправляйте просмотр из `build()`: перестроение виджета не означает новый визит.

`BuildContext` должен относиться к актуальному экрану внутри `MaterialApp`/`Navigator`; для SnackBar нужен `ScaffoldMessenger`. SDK проверяет `context.mounted`, но уход на другой маршрут не всегда размонтирует старый экран. Управляйте моментом вызова в логике навигации приложения.

Чтобы отправить просмотр без встроенного показа:

```dart
final details = await GravitySDK.instance.trackViewNoShow(
  pageContext: productContext,
);
```

`details` — nullable-ответ headless. Если нужно только собирать просмотры/события, задайте `isFetchContentOnTrack: false`; влияние флага описано в [Конфигурации](./configuration.md#isfetchcontentontrack).

## Пользовательские действия

Отправляйте событие после успешного действия: изменения корзины, завершения заказа или авторизации. `triggerEvent()` может показывать in-app; `triggerEventNoShow()` не показывает UI и не требует `BuildContext`.

### Добавление и удаление товара

```dart
await GravitySDK.instance.triggerEventNoShow(
  events: [
    AddToCartEvent(
      value: 1990,
      productId: 'sku-123',
      quantity: 1,
      currency: 'RUB',
      cart: [
        CartItem(productId: 'sku-123', quantity: 1, itemPrice: 1990),
      ],
      customProps: {'list': 'recommendations'},
    ),
  ],
  pageContext: productContext,
);
```

`RemoveFromCartEvent` принимает те же поля. `value` и `itemPrice` — денежные значения, `quantity` — целое количество. Используйте согласованный с аналитикой проекта смысл `value`. Не подменяйте SKU внутренним ID приложения, если фид использует другой идентификатор.

### Покупка

```dart
await GravitySDK.instance.triggerEventNoShow(
  events: [
    PurchaseEvent(
      uniqueTransactionId: 'ORDER-2026-1001-42',
      value: 3980,
      currency: 'RUB',
      cart: [
        CartItem(productId: 'sku-123', quantity: 2, itemPrice: 1990),
      ],
      customProps: {'delivery': 'pickup'},
      eventTime: DateTime.utc(2026, 10, 1, 10, 15),
    ),
  ],
  pageContext: const PageContext(
    type: ContextType.other,
    data: [],
    location: 'app://order/success',
  ),
);
```

`uniqueTransactionId` — устойчивый уникальный ID конкретного заказа. При повторной попытке доставки одного заказа не создавайте новый ID. Это особенно важно для [офлайн-доставки](./offline.md#ограничения-и-повторная-доставка).

### Синхронизация корзины

```dart
final cartEvent = SyncCartEvent(
  value: 3980,
  currency: 'RUB',
  cart: [
    CartItem(productId: 'sku-123', quantity: 2, itemPrice: 1990),
  ],
);
```

Передавайте актуальный состав корзины; пустая корзина — `cart: []`.

### Вход и регистрация

`LoginEvent` и `SignUpEvent` используют `cuid`/`cuidType` или `hashedEmail`. Полный пример хеширования и отправки — в [Идентификации пользователя](./identity.md#авторизация-и-cuid).

### Произвольное событие

```dart
final customEvent = CustomEvent(
  type: 'delivery-selected-v1',
  name: 'Delivery selected',
  customProps: {'method': 'pickup', 'store_id': 'store-12'},
);
```

`type` и `name` согласуйте с настройкой событий/кампаний проекта. `CustomEvent` также принимает `cuid`, `cuidType` и `cart`. Не используйте его для повторного клика по встроенной кнопке SDK, который уже учтён её tracking.

## customProps и eventTime

Эти поля доступны всем типам `TriggerEvent` в 0.24.0:

- `customProps` — `Map<String, String>?`. Числа и boolean преобразуйте в строки; вложенные объекты полем не поддерживаются.
- `eventTime` — `DateTime?`. SDK отправляет время в UTC. Если оно не задано, SDK фиксирует время вызова события, в том числе до ожидания сессии и доставки из очереди.

Не присваивайте событию время очередной попытки отправки. Для сохранённых ранее действий передавайте их фактическое время.

## Ошибки и результат вызова

Возврат `triggerEvent()`/`triggerEventNoShow()` не подтверждает доставку: при временной ошибке запрос может остаться в очереди. `NoShow` возвращает `null` также при отсутствии контента, выключенной его загрузке, устаревшем ответе или обработанной ошибке.

Методы трекинга обрабатывают ошибки запроса внутри SDK. Вызов до `initialize()` выбрасывает исключение. Для запросов `getContentBy*` приложение отдельно обрабатывает сетевые ошибки и fallback.
