---
title: "Обработка данных из флоу в Capacitor SDK"
description: "Сохраняйте и используйте данные, которые пользователи вводят во флоу вашего приложения на Capacitor, с помощью Adapty SDK."
---

> **AI agents**: to search Adapty docs faster and with fewer tokens, install the Adapty skill. Claude Code (self-updating via plugin): `claude plugin marketplace add adaptyteam/adapty-skills && claude plugin install adapty-skills@adapty` — other tools: `npx skills add adaptyteam/adapty-skills --all`

Когда пользователь вводит текст в поле ввода, отвечает на вопрос викторины или переключает тумблер во [флоу](adapty-flow-builder), SDK передаёт значение в ваше приложение через колбэк аналитики.

Чаще всего приложения используют эти данные для:

- **Регистрация пользователей на своём бэкенде**: Возьмите email и имя, которые пользователь ввёл в онбординге, и создайте аккаунт, когда флоу закроется.
- **Сохранение ответов и предпочтений**: Отслеживайте выбор пользователя, чтобы приложение могло использовать его позже — например, [запишите данные в профиль Adapty](capacitor-setting-user-attributes) как пользовательские атрибуты.
- **Настройка будущих флоу**: Сохраняйте ответы на вопросы викторины как пользовательские атрибуты, затем настройте таргетинг на последующий плейсмент, чтобы каждый сегмент видел свой флоу или свой пейвол внутри него.
- **Передача данных в сторонние аналитические платформы**: Отправляйте ответы в Amplitude, Mixpanel или любую другую платформу продуктовой аналитики, которую вы используете.

Поля ввода и группы выбора автоматически передают свои значения. Чтобы различать их в коде, задайте каждому полю ввода уникальный **Element ID**, а каждой группе выбора — **Group ID** в Paywall Builder.

## Прежде чем начать \{#before-you-start\}

Вам понадобится:

- **Adapty SDK v4 или новее**: колбэки флоу отсутствуют в более ранних версиях.
- **Флоу, собранный в [Flow & Paywall Builder](adapty-flow-builder)**: только флоу передают входные значения через этот колбэк.
- **Недавно опубликованная версия флоу**: флоу передаёт входные значения только если вы опубликовали его после того, как эта функция стала доступна. Если ничего не приходит в приложение, опубликуйте новую версию флоу и попробуйте снова.

## Получение входных значений \{#receive-input-values\}

Входные значения поступают в тот же обработчик, что и все остальные аналитические события флоу, под именем события `flow_user_input`. Зарегистрируйте `onAnalytics` вместе с остальными обработчиками событий флоу:

```typescript showLineNumbers title="Capacitor"
view.setEventHandlers({
  onAnalytics(name, params) {
    handleFlowInput(name, params);
    return false; // keep the flow open
  },
});
```

Коллбэк `onAnalytics` передаёт все аналитические события из флоу, включая [просмотры экранов](capacitor-flow-screen-views).
- Параметр `name` содержит название события. Чтобы отфильтровать события пользовательского ввода, сравните `name` со значением `flow_user_input`.
- Параметр `element_type` указывает категорию элемента.
- Значение ввода хранится в разных параметрах в зависимости от типа элемента:
     - Текстовые поля, выпадающие списки и переключатели сохраняют пользовательский ввод в `value`
     - Группы выбора передают активные варианты в `item_ids` и `item_titles`

```typescript showLineNumbers title="Capacitor"
function handleFlowInput(name: string, params: Record<string, unknown>) {
  if (name !== 'flow_user_input') return;

  // The screen the input sits on. Pair it with element_id to tell apart
  // two fields that share an Element ID on different screens.
  const screenId = params.instanceId as string;

  switch (params.element_type) {
    case 'text_input':
    case 'email_input':
    case 'number_input':
    case 'phone_input': {
      const text = params.value as string;
      break;
    }
    case 'date_picker':
    case 'time_picker':
    case 'date_time_picker': {
      // Unix time in milliseconds.
      const date = new Date(params.value as number);
      break;
    }
    case 'single_choice': {
      const optionId = (params.item_ids as string[])[0];
      break;
    }
    case 'multi_choice': {
      const optionIds = params.item_ids as string[];
      break;
    }
    case 'toggle': {
      const isOn = params.value as boolean;
      break;
    }
  }
}
```

Чтобы убедиться, что коллбэк срабатывает, взаимодействуйте с полем ввода в тестовой сборке вашего приложения. Если обработчик коллбэка не получает событие, проверьте [предварительные требования](#before-you-start). Убедитесь, что флоу был опубликован после того, как эта функция стала доступной.

## Когда приложение получает входные данные \{#when-your-app-receives-the-input\}

:::important
Значение по умолчанию или предвыбранный вариант никогда не попадают в ваше приложение через этот колбэк. Если пользователь принимает опцию, отмеченную **Set as default**, и продолжает, событие не срабатывает. Отсутствие события не означает «нет ответа» — пользователь просто оставил значение по умолчанию.
:::

**Следующие элементы вызывают это событие:**

- Текстовые, email-, числовые и телефонные поля
- Выборщики даты, времени и даты со временем
- Группы с единственным и множественным выбором, а также переключатели

**Следующие не вызывают:**

- Поля пароля, выборы продукта и переключения вкладок
- Поля ввода внутри элемента Header, который является общим для всех экранов
- Группы с выбором, у которых есть дублирующиеся или отсутствующие Element ID у опций, либо Group ID, повторно используемый на другом экране. Такая группа не отправляет ничего, а не часть ответа.

**Событие срабатывает когда:**

- Поле теряет фокус. Очищенное поле сообщает пустую строку; поле, которое пользователь ни разу не редактировал, не сообщает ничего. Плейсхолдер не является значением. Если пользователь возвращается к полю, редактирует его и снова уходит, следует второе событие.
- Пользователь закрывает выборщик после выбора нового значения. Закрытие без изменений не сообщает ничего.
- Пользователь нажимает на опцию или переключатель. Событие множественного выбора содержит список всех выбранных опций, поэтому снятие последней выбранной опции отправляет два пустых массива.

**Событие не срабатывает когда:**

- Пользователь набирает текст. Потока нажатий клавиш нет — только значение, которое поле содержит в момент потери фокуса.
- Пользователь отправляет или закрывает флоу. Значение, которое в этот момент ещё редактируется, может быть потеряно; в разделе [ограничения доставки](#delivery-and-limitations) описано, как спроектировать последний экран с учётом этого.
- Значение устанавливается без взаимодействия с пользователем. Опция, отмеченная **Set as default**, предвыбирается при открытии экрана, а действие **Set Variable** может выбрать опцию или заполнить поле ввода из другого взаимодействия. Ни то ни другое не отправляет событие; предзаполненное поле ввода сообщается только после того, как пользователь его отредактирует.

## Что вы получаете \{#what-you-receive\}

Коллбэк доставляет два типа событий. Фильтруйте по `name`, чтобы отображать только события `flow_user_input`. JSON-ответ выглядит так:

```json
{
  "name": "flow_user_input",
  "instanceId": "scr_registration",
  "isBackendEvent": false,
  "isCustomerEvent": true,
  "element_id": "email",
  "element_type": "email_input",
  "value": "jane@example.com"
}
```

| Параметр          | Описание |
|:------------------|:---------|
| `name`            | `flow_user_input` для событий ввода, `flow_screen_showed` для просмотров экрана. |
| `instanceId`      | ID экрана, на котором находится поле ввода. ID элементов уникальны в пределах экрана, но не в рамках всего флоу. Если в вашем флоу есть поля ввода на нескольких экранах, используйте `instanceId` вместе с `element_id` при фильтрации событий. |
| `element_id`      | **Element ID** [поля ввода](builder-inputs-and-forms) или **Group ID** [группы выбора](flow-selectable-elements). |
| `element_type`    | Тип элемента, отправившего событие. Определяет, в каком из параметров ниже хранится значение ввода. |
| `value`           | **Только для текстовых полей, пикеров и переключателей.** Значение ввода: строка для текстовых полей, целое число для пикеров, булево значение для переключателей. |
| `item_ids`        | **Только для групп с единственным и множественным выбором.** **Element ID** выбранных вариантов в порядке их отображения в билдере. Один элемент для группы с единственным выбором; любое количество для группы с множественным выбором. |
| `item_titles`     | **Только для групп с единственным и множественным выбором.** Заголовки вариантов, перечисленных в `item_ids`, в том же порядке. Никогда не пустой: вариант без заголовка возвращает свой ID. |
| `isCustomerEvent` | Служебный флаг, всегда `true` для этого события. Помечает события, которые SDK доставляет в ваш коллбэк. Удобен, если один обработчик пересылает все события флоу в вашу аналитику, а вы фильтруете по этому флагу, а не по `name`. |
| `isBackendEvent`  | Служебный флаг, всегда `false` для этого события. Помечает события, которые Adapty также записывает для собственной аналитики. Значение `false` подтверждает, что введённые пользователями данные поступают только в ваше приложение — Adapty их не получает и не хранит. |

Что передаёт каждый элемент:

| В билдере | `element_type` | Параметр со значением | Что содержит |
|:----------|:---------------|:----------------------|:-------------|
| Ввод **Text**, **Number**, **Phone number** | `text_input`, `number_input`, `phone_input` | `value` | Строка, введённая пользователем. Числа передаются как строки, не как числовые типы. |
| Ввод **E-mail** | `email_input` | `value` | Строка, введённая пользователем, даже если она не прошла проверку формата в билдере. Валидируйте её на своей стороне перед использованием. |
| Ввод **Password** | нет | нет | Не отправляет событий. |
| Ввод **Date** | `date_picker` | `value` | Unix-время в миллисекундах, целое число, соответствующее полуночи выбранной даты по местному времени. |
| Ввод **Time** | `time_picker` | `value` | Unix-время в миллисекундах, целое число, округлённое до минуты. |
| Ввод **Date & Time** | `date_picker` и `time_picker` | `value` | Два элемента: пикер даты и пикер времени. Каждый отправляет собственное событие. |
| Ввод с переключением на **Date & Time** в выпадающем списке **Type** | `date_time_picker` | `value` | Unix-время в миллисекундах, целое число, округлённое до минуты. |
| Группа **Single choice** | `single_choice` | `item_ids`, `item_titles` | Два массива. `item_ids`: массив с Element ID выбранного варианта. `item_titles`: массив с заголовком этого варианта. |
| Группа **Multi-choice** | `multi_choice` | `item_ids`, `item_titles` | Два массива. `item_ids`: Element ID всех выбранных вариантов в порядке их отображения в билдере. `item_titles`: их заголовки в том же порядке. Оба массива пусты, если ничего не выбрано. |
| Группа **Toggle** | `toggle` | `value` | Булево значение. |

Для ветвления по ответу сравнивайте `item_ids`, а не `item_titles`. Заголовок — производная величина: **Element Title** варианта, если он задан; иначе его текст в вашей локали по умолчанию; иначе его Element ID. Пользователь, просматривавший флоу на другом языке, видел другой текст.

## Примеры событий \{#event-examples\}

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

<Details>
<summary>Ввод текста, email, числа и телефона (нажмите для раскрытия)</summary>

```typescript
onAnalytics(name, params) {
    name;                      // 'flow_user_input'
    params.name;               // 'flow_user_input'
    params.instanceId;         // 'scr_J260KU5q'
    params.isCustomerEvent;    // true
    params.isBackendEvent;     // false
    params.element_id;         // 'email'
    params.element_type;       // 'email_input'
    params.value;              // 'jane@example.com'   (string)
}
```
</Details>

<Details>
<summary>Выбор даты, времени и даты-времени (Нажмите, чтобы развернуть)</summary>

```typescript
onAnalytics(name, params) {
    params.element_id;         // 'birthday'
    params.element_type;       // 'date_picker'
    params.value;              // 645408000000   (Unix milliseconds — 1990-06-15, local midnight)
}
```
</Details>

<Details>
<summary>Single choice (Click to expand)</summary>

```typescript
onAnalytics(name, params) {
    params.element_id;         // 'experience'
    params.element_type;       // 'single_choice'
    params.item_ids;           // ['pro']
    params.item_titles;        // ['I train professionally']
}
```
</Details>

<Details>
<summary>Multi choice (Нажмите, чтобы развернуть)</summary>

```typescript
onAnalytics(name, params) {
    params.element_id;         // 'interests'
    params.element_type;       // 'multi_choice'
    params.item_ids;           // ['sports', 'music']
    params.item_titles;        // ['Sports', 'Music']
}
```
</Details>

<Details>
<summary>Toggle (Нажмите, чтобы развернуть)</summary>

```typescript
onAnalytics(name, params) {
    params.element_id;         // 'reminders'
    params.element_type;       // 'toggle'
    params.value;              // true   (boolean)
}
```
</Details>

## Доставка и ограничения \{#delivery-and-limitations\}

:::warning
Флоу передают необработанные значения — адреса электронной почты, номера телефонов и всё, что вводит пользователь. Относитесь ко всему, что приходит в аналитический колбэк, как к персональным данным, и не записывайте это туда, куда бы вы не записали email пользователя.
:::

- **Побеждает последнее значение**: вы получаете одно событие на поле с тем значением, на котором пользователь остановился, а не поток каждого нажатия клавиши. Если он редактирует поле, до вас дойдёт только последняя версия.
- **Нет гарантии отправки**: значения поступают к вам по мере того, как пользователь движется по флоу, и он может выйти в любой момент. Дождитесь закрытия флоу, прежде чем считать набор ответов завершённым.
- **Доставка по возможности**: если пользователь закрывает флоу или сворачивает приложение, пока поле ещё в фокусе или пикер открыт, это значение может быть потеряно.

Чтобы значение последнего поля было надёжным, завершайте флоу экраном без полей ввода и пикеров, и закрывайте флоу явным действием пользователя, а не автоматически при появлении этого экрана. Переход на финальный экран снимает фокус с предыдущего поля — именно это и отправляет его значение.

Сохраняйте каждое входное значение по мере того, как его получает обработчик, и отправляйте полный набор при закрытии флоу. Чтобы отследить этот момент, зарегистрируйте обработчик `onDisappeared` вместе с `onAnalytics`. Он срабатывает, когда представление флоу скрывается — независимо от того, завершил ли пользователь флоу или закрыл его на полпути.

## Варианты использования \{#use-cases\}

### Регистрация пользователей на вашем бэкенде \{#register-users-on-your-backend\}

Собирайте значения по мере их поступления и отправляйте их одним запросом после закрытия флоу, чтобы передать полный набор ответов.

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

Флоу не может отображать ошибки, полученные от бэкенда. Возвращаемое значение обработчика только закрывает флоу, и в SDK нет метода для передачи данных в уже запущенный флоу. Если регистрация не удалась — например, потому что email уже используется — покажите ошибку в своём интерфейсе после закрытия флоу.

```typescript showLineNumbers title="Capacitor"
const flowAnswers: Record<string, string> = {};

view.setEventHandlers({
  onAnalytics(name, params) {
    if (name === 'flow_user_input' && typeof params.value === 'string') {
      flowAnswers[params.element_id as string] = params.value;
    }
    return false;
  },
  onDisappeared() {
    if (flowAnswers.email) {
      // Send flowAnswers to your backend here to create the account.
    }
    return false;
  },
});
```

### Обогащение профилей пользователей данными \{#enrich-user-profiles-with-data\}

Чтобы связать введённые пользователем данные с его профилем и не запрашивать одни и те же сведения повторно, [обновляйте профиль пользователя](capacitor-setting-user-attributes) по мере их поступления.

Например, если в вашем флоу есть текстовое поле с ID элемента `name` и поле email с ID элемента `email`:

```typescript showLineNumbers title="Capacitor"
function handleFlowInput(name: string, params: Record<string, unknown>) {
  if (name !== 'flow_user_input') return;
  if (typeof params.value !== 'string') return;

  const profileParams: Partial<AdaptyProfileParameters> = {};

  switch (params.element_id) {
    case 'name':
      profileParams.firstName = params.value;
      break;
    case 'email':
      profileParams.email = params.value;
      break;
    default:
      return;
  }

  adapty.updateProfile(profileParams).catch((error) => {
    // handle the error
  });
}
```

### Настройка флоу, показываемых позже \{#customize-flows-shown-later\}

Ответы на вопросы квиза также могут определять, что пользователь увидит в другом [плейсменте](placements) — другой флоу или другой пейвол внутри него.

Например, спросите пользователей об их опыте занятий спортом в онбординг-флоу, а затем покажите каждой группе свой флоу с разными продуктами и текстом.

1. Добавьте [квиз](onboarding-quizzes) в ваш флоу. Назначьте [выбираемой группе](flow-selectable-elements) Group ID `experience`, а каждому варианту — понятный Element ID.
2. Обработайте ответы и [задайте пользовательские атрибуты](capacitor-setting-user-attributes) для пользователя.

```typescript showLineNumbers title="Capacitor"
function handleFlowInput(name: string, params: Record<string, unknown>) {
  if (name !== 'flow_user_input') return;
  if (params.element_id !== 'experience') return;

  const optionId = (params.item_ids as string[])[0];

  adapty
    .updateProfile({
      // Set the custom attribute 'experience' to the option the user selected
      // (beginner, amateur, or pro).
      codableCustomAttributes: { experience: optionId },
    })
    .catch((error) => {
      // handle the error
    });
}
```

3. [Создайте сегмент](segments) для каждого значения пользовательского атрибута.
4. Создайте [плейсмент](placements) и добавьте [аудиторию](audience) для каждого сегмента.
5. [Отобразите флоу](capacitor-present-paywalls) для этого плейсмента в приложении.