Передача данных во флоу во Flutter SDK

Кастомный тег — это пользовательская переменная, значение которой задаётся в вашем приложении. Если в настройках переменной выбрать Value comes from outside the app, приложение сможет передавать её значение через Adapty SDK. Например, текстовый элемент содержит «Coins:» и кастомный тег. Если приложение передаёт «120» для этого тега, пользователь видит «Coins: 120».

Прежде чем начать

Вам необходим Adapty SDK версии 4 или выше. Версии ниже v4 не отображают флоу.

Добавьте пользовательские теги во флоу

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

  1. Создайте кастомный тег для каждого значения, которое передаёт ваше приложение.

    Форма создания переменной с именем, описанием, типом значения String и начальным значением
  2. Вставьте каждый тег в текстовый элемент.

  3. Запишите полное имя каждого тега, включая его группу. Именно под этим именем код вашего приложения передаёт каждое значение. Например, если группа user содержит тег streak, полное имя тега — user.streak.

    Вкладка Custom в выпадающем списке Add variable с тегом streak в группе user

Передача значений пользовательских тегов

Передайте значения из вашего приложения в параметр customTags методов AdaptyUI().createFlowView или AdaptyUIFlowPlatformView в виде Map<String, String>:

  • Каждый ключ — это полное имя пользовательского тега, например user.streak.
  • Каждое значение — это строка, которую видят пользователи вместо тега.
  • Каждое значение должно быть удобочитаемым, например “1,250”, а не “1250.0”. Adapty не форматирует и не переводит его.

SDK считывает значения один раз — при создании представления, поэтому они не изменяются, пока флоу открыт.

Чтобы протестировать пользовательские теги, откройте флоу в своём приложении после добавления кода, который устанавливает их значения. Холст конструктора и предпросмотр на устройствах отображают только начальные значения: мобильное приложение Adapty не устанавливает значения пользовательских тегов.

Если ваше приложение уже задаёт значения пользовательских тегов для пейволов в старом Paywall Builder, ознакомьтесь с отличиями пользовательских тегов в флоу.

Что видят пользователи при отсутствии значения

Пользователи видят Начальное значение пользовательского тега как резервное в следующих случаях:

  • Ваше приложение не передаёт никакого значения для тега.
  • Пользователь не обновил приложение до версии, которая передаёт это значение.
  • Имя в коде вашего приложения не совпадает с полным именем тега, включая регистр букв.
  • Другой пользовательский тег в том же текстовом элементе не имеет значения. В этом случае каждый пользовательский тег в этом текстовом элементе показывает своё начальное значение.

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

Пример: показываем ИМТ из ответов онбординга

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

  1. Флоу собирает ответы и передаёт их в ваше приложение.
  2. Приложение вычисляет результат.
  3. Приложение передаёт результат в следующий флоу в виде значения кастомного тега.

В этом примере онбординг-флоу собирает вес и рост пользователя. Когда пользователь закрывает онбординг-флоу после последнего экрана, приложение вычисляет индекс массы тела (ИМТ). Затем открывается пейвол-флоу, который показывает этот ИМТ.

Настройка флоу

  1. Создайте два флоу: онбординг-флоу, собирающий вес и рост пользователя, и флоу-пейвол, показывающий его ИМТ.

  2. Добавьте два поля Number в онбординг-флоу:

    • Одно для веса в килограммах с Element ID weight.
    • Одно для роста в сантиметрах с Element ID height.

    Для обоих полей установите Format в значение Integer. Поле отправляет значение как текст, точно так, как его ввёл пользователь, а пример кода не воспринимает «70.5» и «70,5» одинаково. Целые числа не содержат разделителя дробной части, поэтому код читает их одинаково в любой локали.

  3. Чтобы убедиться, что приложение получает оба значения, завершите онбординг-флоу экраном без полей ввода. На этом экране добавьте кнопку с действием Close flow. Поля отправляют свои значения при потере фокуса или при переходе пользователя на другой экран. Действие Close flow не отправляет их. Пример кода открывает пейвол только в том случае, если пользователь добрался до последнего экрана.

  4. В флоу-пейволе создайте кастомный тег с именем bmi. В поле Initial value введите тире (—). Пользователь увидит тире, если приложение не передало значение ИМТ.

  5. Добавьте текстовый элемент со словами «Your BMI:» в флоу-пейвол. После этих слов вставьте кастомный тег bmi.

  6. Добавьте онбординг-флоу в плейсмент, например onboarding. Добавьте флоу-пейвол в плейсмент с ID bmi_paywall. Код из раздела Pass the BMI to the paywall получает пейвол по этому ID.

Передайте BMI на пейвол

Собирайте вес и рост из событий flow_user_input флоу онбординга, как описано в Обработке данных из флоу. Чтобы понять, что пользователь дошёл до последнего экрана, отслеживайте событие flow_screen_showed с параметром is_last_screen, равным true, как описано в Отслеживании просмотров экранов флоу. Когда пользователь закроет флоу онбординга после этого экрана, рассчитайте BMI. Затем создайте вью пейвола и передайте BMI для кастомного тега bmi.

Добавьте следующие методы в наблюдатель, который вы регистрируете с помощью AdaptyUI().setFlowsEventsObserver. Наблюдатель также должен реализовывать flowViewDidFinishRestore и flowViewDidReceiveError, которые в этом примере опущены:

final Map<String, String> bodyAnswers = {};
bool reachedLastScreen = false;

void flowViewDidReceiveAnalyticEvent(
  AdaptyUIFlowView view,
  String name,
  Map<String, dynamic> params,
) {
  if (name == 'flow_screen_showed' && params['is_last_screen'] == true) {
    reachedLastScreen = true;
    return;
  }
  if (name != 'flow_user_input') return;

  final elementId = params['element_id'];
  final value = params['value'];
  if (elementId is! String || value is! String) return;

  bodyAnswers[elementId] = value;
}

void flowViewDidDisappear(AdaptyUIFlowView view) {
  // The observer receives events from every flow. Reset `bodyAnswers` and
  // `reachedLastScreen` whenever a flow disappears, so that the paywall
  // doesn't open again when the user closes it.
  final answers = Map<String, String>.of(bodyAnswers);
  final finished = reachedLastScreen;
  bodyAnswers.clear();
  reachedLastScreen = false;

  // Number inputs send their values as strings.
  final weight = double.tryParse(answers['weight'] ?? '');
  final height = double.tryParse(answers['height'] ?? '');
  if (!finished || weight == null || height == null || height <= 0) return;

  final heightInMeters = height / 100;
  final bmi = weight / (heightInMeters * heightInMeters);
  showBmiPaywall(bmi.toStringAsFixed(1));
}

Future<void> showBmiPaywall(String bmi) async {
  try {
    final flow = await Adapty().getFlow(placementId: 'bmi_paywall');
    final view = await AdaptyUI().createFlowView(
      flow: flow,
      customTags: {'bmi': bmi},
    );
    await view.present();
  } on AdaptyError catch (e) {
    // handle the error
  } catch (e) {
    // handle the error
  }
}

toStringAsFixed всегда использует точку в качестве разделителя дробной части. Чтобы использовать разделитель, соответствующий локали пользователя, отформатируйте ИМТ с помощью NumberFormat из пакета intl.

Сохранение ИМТ в профиль пользователя

Чтобы показывать пользователям разные флоу в зависимости от их ИМТ, сохраните его как кастомный атрибут профиля. В дашборде Adapty создайте кастомный атрибут типа Number с ключом bmi. Затем показывайте каждый флоу нужной аудитории, сформированной на основе этого атрибута.

Вызовите метод updateProfile внутри flowViewDidDisappear, сразу после строки, вычисляющей bmi. Передавайте именно double, а не отформатированную строку — так сегмент сможет сравнивать ИМТ с пороговым значением:

try {
  final builder = AdaptyProfileParametersBuilder()
    ..setCustomDoubleAttribute(bmi, 'bmi');
  await Adapty().updateProfile(builder.build());
} on AdaptyError catch (e) {
  // handle the error
} catch (e) {
  // handle the error
}