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

SDK поддерживает анонимный профиль, склейку по CUID и ручную идентификацию. Не смешивайте эти идентификаторы:

Идентификатор Назначение
Серверный uid Внутренний профиль Gravity Field; SDK сохраняет его на устройстве
ses Сессия запросов
cuid и cuidType Единый идентификатор клиента между Web, приложением и офлайн-данными
userId в setUser() Внешний ID приложения; передаётся как user.custom

Общая модель: Идентификация и омниканальность.

Анонимный профиль

Не вызывайте setUser(), если хотите стандартное управление идентификацией. При первом успешном запросе сервер возвращает идентификаторы, а SDK сохраняет и использует их в последующих запросах. Не создавайте новую сессию вручную на каждый экран.

Авторизация и CUID

После успешной авторизации отправьте LoginEvent. При регистрации можно использовать SignUpEvent. Рекомендуемый CUID — SHA-256 нормализованного телефона с cuidType: 'phone_hash'.

Номер нормализуют в международный формат без + и разделителей. Для РФ/КЗ — 7XXXXXXXXXX. Хеш вычисляют по UTF-8 строке и передают как lowercase hex. Если CDP/DWH уже хранит такой идентификатор, используйте тот же хеш; не пересчитывайте его по другому правилу.

Добавьте crypto: ^3.0.6 в зависимости. Пример для номеров РФ/КЗ:

import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:gravity_sdk/gravity_sdk.dart';

String phoneHashRuKz(String rawPhone) {
  var digits = rawPhone.replaceAll(RegExp(r'[^0-9]'), '');
  if (digits.length == 11 && digits.startsWith('8')) {
    digits = '7' + digits.substring(1);
  }
  if (digits.length != 11 || !digits.startsWith('7')) {
    throw ArgumentError('Нужен полный номер РФ/КЗ в международном формате');
  }
  return sha256.convert(utf8.encode(digits)).toString();
}

Future<void> identifyAfterLogin(String phone) async {
  await GravitySDK.instance.triggerEventNoShow(
    events: [
      LoginEvent(cuid: phoneHashRuKz(phone), cuidType: 'phone_hash'),
    ],
    pageContext: const PageContext(
      type: ContextType.other,
      data: [],
      location: 'app://login',
    ),
  );
}

Для других стран нормализацию выполняйте по правилам их телефонных номеров. Склейка происходит на сервере после доставки события; при офлайн-отправке не считайте её завершённой сразу. Успешный возврат triggerEventNoShow() не является подтверждением авторизации или доставки.

SDK допускает nullable-поля у Login/SignUp, но серверу нужен идентификатор: cuid вместе с cuidType либо hashedEmail. Не отправляйте пустое событие.

Ручной пользователь

GravitySDK.instance.setUser('customer-42', 'app-session-2026-10-01');

Приложение само управляет внешним ID и сроком жизни этой сессии. Не меняйте session ID на каждый запрос и не используйте константу для всех клиентов. setUser() не выполняет запрос к серверу и не отправляет LoginEvent.

В 0.24.0 этот пользователь применяется и к просмотрам/событиям, и ко всем запросам контента, inline-блокам и автопоказу in-app. Сам внешний ID не заменяет CUID для омниканальной склейки. getUserId() не возвращает значение из setUser().

Выход и смена аккаунта

await GravitySDK.instance.resetUser();

Метод убирает ручного пользователя и сбрасывает серверный UID/сессию. Следующий запрос начинает новую анонимную сессию. Дождитесь завершения сброса перед запросами нового аккаунта.

resetUser() не очищает офлайн-очередь: события прежнего пользователя должны доставляться с его исходной идентификацией. Если ваша политика для общего устройства требует удалить ожидающие события:

await GravitySDK.instance.clearQueue();
await GravitySDK.instance.resetUser();

На время смены аккаунта приложение должно остановить отправку новых событий и пересоздать персонализированные виджеты/состояния. Уже загруженный UI не обновляется автоматически только от вызова setUser() или resetUser(). clearQueue() удаляет данные без возможности восстановления и не отменяет запрос уже в сети.

Получение серверного UID

final String? uid = await GravitySDK.instance.getUserId();

GravitySDK.instance.setUserIdListener((String? uid) {
  // В этом примере только наблюдаем изменение.
  print('Server UID: ' + (uid ?? 'не задан'));
});

// Когда подписка больше не нужна:
GravitySDK.instance.setUserIdListener(null);

До первого успешного запроса getUserId() может вернуть null. Если инициализация сессии уже выполняется, метод дожидается её; ошибку ожидания обрабатывайте в вызывающем коде.

Listener сообщает о появлении/изменении известного процессу UID, включая сброс на null и восстановление. Повторный серверный ответ с тем же UID его не вызывает. Подписка одна; новый listener заменяет старый. Чтобы получить текущее сохранённое значение, отдельно вызовите getUserId() — подписка не заменяет чтение.

Восстановление после переустановки

Если приложение сохранило серверный UID вне удаляемого локального хранилища, его можно передать перед первым запросом:

Future<void> restoreProfile(String savedServerUid) async {
  await GravitySDK.instance.restoreUserId(savedServerUid);
}

restoreUserId() принимает серверный UID, полученный через getUserId(). Он сбрасывает текущую сессию и пользователя из setUser(); следующий запрос передаёт восстановленный UID серверу. Неизвестный серверу UID не восстанавливает профиль — сервер присваивает новый.

Пустая строка вызывает ArgumentError. Методы чтения UID, подписки и восстановления доступны до initialize(). Восстановление само по себе не проверяет UID на сервере. Хранение внешней копии UID и её удаление при выходе организует приложение.