Передача данных во флоу в Android 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

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

Создайте AdaptyUiTagResolver и передайте его в параметр tagResolver метода AdaptyUI.getFlowView. SDK вызывает резолвер с полным именем каждого кастомного тега, например user.streak. Резолвер возвращает одно из следующих значений:

  • Строка: пользователи видят её вместо тега. Она должна быть удобочитаемой, например «1 250», а не «1250.0». Adapty не форматирует и не переводит её.
  • null: пользователи видят начальное значение тега как запасной вариант.
  • Пустая строка: пользователи не видят ничего вместо тега.

Если вы создаёте AdaptyFlowView самостоятельно, передайте резолвер в его метод showFlow. В Jetpack Compose передайте его в AdaptyFlowScreen. Примеры каждого вызова смотрите в разделе Отображение флоу и пейволов.

Чтобы протестировать пользовательские теги, откройте флоу в своём приложении после добавления кода, который устанавливает их значения. Холст конструктора и предпросмотр на устройствах отображают только начальные значения: мобильное приложение 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.

Передайте следующий листенер в представление флоу онбординга:

class OnboardingEventListener(
    private val onBmiCalculated: (bmi: String) -> Unit,
) : AdaptyFlowDefaultEventListener() {

    private val bodyAnswers = mutableMapOf<String, String>()
    private var reachedLastScreen = false

    override fun onAnalyticEvent(
        name: String,
        params: Map<String, Any?>,
        context: Context,
    ) {
        if (name == "flow_screen_showed" && params["is_last_screen"] == true) {
            reachedLastScreen = true
            return
        }
        if (name != "flow_user_input") return

        val elementId = params["element_id"] as? String ?: return
        val value = params["value"] as? String ?: return

        bodyAnswers[elementId] = value
    }

    override fun onFlowClosed() {
        // Reset `bodyAnswers` and `reachedLastScreen` on every close,
        // so that the next run starts without old answers.
        val answers = bodyAnswers.toMap()
        val finished = reachedLastScreen
        bodyAnswers.clear()
        reachedLastScreen = false

        // Number inputs send their values as strings.
        if (!finished) return
        val weight = answers["weight"]?.toDoubleOrNull() ?: return
        val height = answers["height"]?.toDoubleOrNull()?.takeIf { it > 0 } ?: return

        val heightInMeters = height / 100
        val bmi = weight / (heightInMeters * heightInMeters)

        onBmiCalculated(String.format(Locale.getDefault(), "%.1f", bmi))
    }
}

Когда пользователь закрывает флоу онбординга после последнего экрана, слушатель вызывает onBmiCalculated с BMI. Передайте лямбду для onBmiCalculated, которая вызывает следующую функцию. Функция получает флоу пейвола, создаёт его представление и передаёт BMI для кастомного тега bmi:

fun showBmiPaywall(activity: Activity, bmi: String) {
    Adapty.getFlow("bmi_paywall") { flowResult ->
        when (flowResult) {
            is AdaptyResult.Success -> {
                AdaptyUI.getFlowConfiguration(flowResult.value) { configResult ->
                    when (configResult) {
                        is AdaptyResult.Success -> {
                            val flowView = AdaptyUI.getFlowView(
                                activity,
                                configResult.value,
                                products = null,
                                eventListener = AdaptyFlowDefaultEventListener(),
                                tagResolver = AdaptyUiTagResolver { tag ->
                                    if (tag == "bmi") bmi else null
                                },
                            )
                            // add flowView to your view hierarchy
                        }
                        is AdaptyResult.Error -> {
                            // handle the error
                        }
                    }
                }
            }
            is AdaptyResult.Error -> {
                // handle the error
            }
        }
    }
}

String.format с Locale.getDefault() использует десятичный разделитель локали пользователя: “22.7” для английского, “22,7” для немецкого.

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

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

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

val params = AdaptyProfileParameters.Builder()
    .withCustomAttribute("bmi", bmi)
    .build()

Adapty.updateProfile(params) { error ->
    if (error != null) {
        // handle the error
    }
}