# Сентябрь '26. Flutter SDK: события без сети и точная персонализация

Во Flutter SDK вышли версии `0.23.0`–`0.24.0`. События теперь можно доставлять после восстановления соединения, приложению стало проще работать с произвольными данными кампании, а запросы контента учитывают пользователя, заданного через `setUser()`.

## События сохраняются, когда пропадает сеть

SDK сохраняет события на устройстве до отправки на сервер. Если соединение прервалось, он повторит отправку позже: при следующем запуске приложения, возвращении на передний план или восстановлении связи. Это касается событий из `triggerEvent()`, `triggerEventNoShow()` и ответов in-app форм.

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

Очередь включена по умолчанию. Если приложение само отслеживает доступность сети, отправку можно запустить вручную:

```dart
await GravitySDK.instance.flushQueue();
final pending = await GravitySDK.instance.pendingDeliveries;
```

Для очереди доступны ограничения по числу и сроку хранения событий через `OfflineQueueSettings`. Метод `clearQueue()` удаляет ожидающие отправки события, например при выходе из аккаунта на общем устройстве. `resetUser()` сам по себе очередь не очищает.

Если событие дошло до сервера, но ответ потерялся, повторная отправка может создать дубль. Для покупок используйте уникальный `uniqueTransactionId`. Очередь работает, пока приложение запущено; просмотры экранов, запросы контента и взаимодействия с контентом в неё не попадают.

## У событий появились свойства и точное время

Любому событию теперь можно передать `customProps` и `eventTime`. Это помогает сохранить контекст действия — например, из какого списка товар добавили в корзину — и указать фактическое время события:

```dart
AddToCartEvent(
  value: 1990,
  productId: 'sku-123',
  quantity: 1,
  customProps: {'list': 'search'},
  eventTime: DateTime.now(),
);
```

Значения в `customProps` должны быть строками. Если `eventTime` не указан, SDK проставит время вызова автоматически. У `CustomEvent` также появились поля `cuid`, `cuidType` и `cart`.

## Больше возможностей при собственной отрисовке кампаний

Приложение может получить весь объект переменных кампании через `CampaignContent.rawVariables`. Это полезно для headless-сценариев и A/B-тестов, когда интерфейс строится в приложении, а Gravity Field передаёт вариант и его параметры. Типизированные поля `content.variables` продолжают работать как раньше.

Для контента, который приложение отрисовывает самостоятельно, появился `ContentClickEngagement`: с его помощью можно отправить клик в аналитику Gravity Field. Для элементов, которые показывает сам SDK, дополнительно отправлять клик не нужно — их действия уже фиксируют взаимодействие.

SDK также повторяет запросы при временных сетевых ошибках и ограничивает запоздалый автопоказ кампаний настройкой `staleContentTimeout`. По умолчанию порог составляет 10 секунд: если ответ пришёл слишком поздно, сообщение не появится поверх уже изменившегося экрана.

## Контент привязан к нужному пользователю

В версии `0.24.0` исправлена работа `setUser()`: запросы контента, в том числе для inline-виджетов и автоматического показа in-app кампаний, теперь выполняются от имени заданного пользователя. Ранее эта идентификация применялась к просмотрам и событиям, но запрос контента мог использовать анонимную сессию устройства. Из-за этого выбор контента, ограничения частоты показа и A/B-вариант могли рассчитываться для другого профиля.

Появились методы для работы с серверным `uid`:

- `getUserId()` возвращает известный SDK серверный идентификатор;
- `setUserIdListener()` сообщает, когда он появился или изменился;
- `restoreUserId()` позволяет восстановить прежний профиль, например после переустановки приложения, если его `uid` был сохранён отдельно.

Серверный `uid` отличается от идентификатора, который приложение передаёт в `setUser()`. При восстановлении неизвестного серверу `uid` будет создан новый профиль.

## Совместимость и обновление

Промежуточная версия `0.23.1` исправила ошибку сборки с `dio` 5.10.0 и новее. Для всех перечисленных изменений обновите Flutter SDK до `0.24.0`. Минимальные требования остаются прежними: Flutter 3.38.1 и Dart 3.10.

Подробности и примеры: [README Flutter SDK](https://github.com/Gravity-Field/gravity-sdk-flutter/blob/main/README.md) и [история изменений SDK](https://github.com/Gravity-Field/gravity-sdk-flutter/blob/main/CHANGELOG.md).
