---
title: "Truyền dữ liệu vào flow trong Android SDK"
description: "Truyền giá trị từ ứng dụng Android của bạn vào một flow và hiển thị nó trong thành phần văn bản."
---

> **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`

[Custom tag](flow-custom-tags) là một biến tùy chỉnh có giá trị lấy từ ứng dụng của bạn. Sau khi bạn chọn **Value comes from outside the app** trong cài đặt biến, ứng dụng của bạn có thể thiết lập giá trị đó thông qua Adapty SDK. Ví dụ: một thành phần văn bản chứa "Coins:" và một custom tag. Nếu ứng dụng của bạn truyền "120" cho tag đó, người dùng sẽ thấy "Coins: 120".

## Trước khi bắt đầu \{#before-you-start\}

Bạn cần Adapty SDK v4 trở lên. Các phiên bản trước v4 không hiển thị flow.

## Thêm thẻ tùy chỉnh vào flow \{#add-custom-tags-to-the-flow\}

Để hiển thị các giá trị của ứng dụng trong một flow, hãy thiết lập custom tag trong builder:

1. [Tạo một custom tag](flow-custom-tags#create-a-custom-tag) cho mỗi giá trị mà ứng dụng của bạn truyền vào.

   <ZoomImage id="flow-custom-tag-create.webp" width="500px" alt="Create variable form with a name, a description, the String value type, and an initial value" />

2. [Chèn từng tag vào một phần tử văn bản](flow-custom-tags#add-a-custom-tag-to-a-text-element).
3. Ghi lại tên đầy đủ của từng tag, bao gồm cả nhóm của nó. Code trong ứng dụng của bạn truyền từng giá trị theo tên này. Ví dụ: nếu nhóm `user` chứa tag `streak`, thì tên đầy đủ của tag là `user.streak`.

   <ZoomImage id="flow-custom-tag-full-name.webp" width="500px" alt="Custom tab of the Add variable dropdown with the streak tag in the user group" />

## Truyền giá trị thẻ tùy chỉnh \{#pass-custom-tag-values\}

Tạo một `AdaptyUiTagResolver` và truyền vào tham số `tagResolver` của `AdaptyUI.getFlowView`. SDK sẽ gọi resolver với tên đầy đủ của từng thẻ tùy chỉnh, ví dụ như `user.streak`. Resolver trả về một trong các giá trị sau:

- **Một chuỗi ký tự**: Người dùng sẽ thấy chuỗi này thay cho thẻ. Giá trị phải ở dạng dễ đọc, ví dụ "1,250" thay vì "1250.0". Adapty không tự định dạng hay dịch giá trị này.
- **`null`**: Người dùng sẽ thấy giá trị Mặc định của thẻ như một fallback.
- **Chuỗi rỗng**: Người dùng sẽ không thấy gì thay cho thẻ.

<Tabs groupId="current-os" queryString>
<TabItem value="kotlin" label="Kotlin" default>

```kotlin showLineNumbers title="Kotlin"
val customTags = mapOf(
    "username" to "DragonSlayer42",
    "coins" to "1,250",
)
val tagResolver = AdaptyUiTagResolver { tag -> customTags[tag] }

val flowView = AdaptyUI.getFlowView(
    activity,
    flowConfiguration,
    products = null,
    eventListener = eventListener,
    tagResolver = tagResolver,
)
```

</TabItem>
<TabItem value="java" label="Java">

```java showLineNumbers title="Java"
Map<String, String> customTags = new HashMap<>();
customTags.put("username", "DragonSlayer42");
customTags.put("coins", "1,250");
AdaptyUiTagResolver tagResolver = customTags::get;

AdaptyFlowView flowView = AdaptyUI.getFlowView(
        activity,
        flowConfiguration,
        null,
        eventListener,
        AdaptyFlowInsets.Unspecified,
        AdaptyCustomAssets.Empty,
        tagResolver
);
```

</TabItem>
</Tabs>

Nếu bạn tự tạo `AdaptyFlowView`, hãy truyền resolver vào phương thức `showFlow` của nó. Trong Jetpack Compose, hãy truyền vào `AdaptyFlowScreen`. Để xem ví dụ cho từng cách gọi, đọc [Hiển thị flow & paywall](android-present-paywalls).

Để kiểm tra các custom tag của bạn, hãy mở flow trong ứng dụng sau khi thêm code thiết lập giá trị cho chúng. Canvas của builder và [xem trước trên thiết bị](paywall-device-compatibility-preview#preview-on-devices) chỉ hiển thị giá trị ban đầu: ứng dụng Adapty trên thiết bị di động không thiết lập giá trị cho custom tag.

Nếu ứng dụng của bạn đã thiết lập giá trị custom tag cho các paywall của Paywall Builder cũ, hãy xem lại [sự khác biệt của custom tag trong flow](migrate-to-flows#custom-tags-from-the-old-paywall-builder).

## Người dùng thấy gì khi không có giá trị \{#what-users-see-without-a-value\}

Người dùng sẽ thấy **Giá trị ban đầu** của thẻ tùy chỉnh như một giá trị dự phòng trong các trường hợp sau:

- Ứng dụng của bạn không truyền giá trị nào cho thẻ đó.
- Người dùng chưa cập nhật ứng dụng lên phiên bản có truyền giá trị.
- Tên trong code của ứng dụng không khớp với tên đầy đủ của thẻ, bao gồm cả chữ hoa/thường.
- Một thẻ tùy chỉnh khác trong cùng phần tử văn bản không có giá trị. Khi đó, mọi thẻ tùy chỉnh trong phần tử văn bản đó đều hiển thị giá trị ban đầu của chúng.

Để không hiển thị gì thay cho thẻ, hãy truyền một chuỗi rỗng. Adapty coi chuỗi rỗng là một giá trị hợp lệ, vì vậy giá trị dự phòng sẽ không xuất hiện.

## Ví dụ: hiển thị chỉ số BMI từ câu trả lời onboarding \{#example-show-a-bmi-from-onboarding-answers\}

Một flow có thể thu thập câu trả lời từ người dùng, và một custom tag có thể hiển thị giá trị từ ứng dụng của bạn. Kết hợp cả hai để hiển thị cho người dùng một kết quả mà ứng dụng của bạn tính toán từ câu trả lời của họ, chẳng hạn như điểm số, gói được đề xuất, hoặc chỉ số sức khỏe:

1. Một flow thu thập câu trả lời và gửi chúng đến ứng dụng của bạn.
2. Ứng dụng của bạn tính toán kết quả.
3. Ứng dụng của bạn truyền kết quả đó sang flow tiếp theo dưới dạng giá trị custom tag.

Trong ví dụ này, một flow onboarding thu thập cân nặng và chiều cao của người dùng. Khi người dùng đóng flow onboarding sau màn hình cuối cùng, ứng dụng của bạn tính chỉ số khối cơ thể (BMI) của họ. Sau đó, ứng dụng mở một flow paywall để hiển thị chỉ số BMI đó.

### Thiết lập các flow \{#set-up-the-flows\}

1. [Tạo hai flow](paywall-builder-templates): một flow onboarding thu thập cân nặng và chiều cao của người dùng, và một flow paywall hiển thị chỉ số BMI của họ.
2. Thêm hai ô nhập [**Number**](builder-inputs-and-forms) vào flow onboarding:
   - Một ô để nhập cân nặng tính bằng kilogram, với **Element ID** là `weight`.
   - Một ô để nhập chiều cao tính bằng centimeter, với **Element ID** là `height`.

   Với cả hai ô nhập, đặt **Format** thành **Integer**. Ô nhập gửi giá trị dưới dạng văn bản, đúng như người dùng đã nhập, và [code mẫu](#pass-the-bmi-to-the-paywall) không đọc "70.5" và "70,5" theo cùng một cách. Số nguyên không có dấu phân cách thập phân, vì vậy code đọc chúng giống nhau ở mọi ngôn ngữ.
3. Để đảm bảo ứng dụng nhận được cả hai giá trị, hãy kết thúc flow onboarding bằng một màn hình không có ô nhập nào. Trên màn hình đó, thêm một nút với hành động [**Close flow**](onboarding-actions#close-flow). Các ô nhập gửi giá trị khi mất tiêu điểm hoặc khi người dùng chuyển sang màn hình khác. Hành động Close flow không gửi chúng. Code mẫu chỉ mở paywall nếu người dùng đã đến màn hình cuối cùng.
4. Trong flow paywall, [tạo một custom tag](flow-custom-tags#create-a-custom-tag) có tên `bmi`. Trong trường **Initial value**, nhập một dấu gạch ngang (`—`). Người dùng sẽ thấy dấu gạch ngang này nếu ứng dụng của bạn không truyền BMI.
5. Thêm một phần tử văn bản với nội dung "Your BMI:" vào flow paywall. Sau những từ này, [chèn custom tag `bmi`](flow-custom-tags#add-a-custom-tag-to-a-text-element).
6. Thêm flow onboarding vào một [placement](placements), chẳng hạn như `onboarding`. Thêm flow paywall vào một placement có ID là `bmi_paywall`. Code trong phần [Pass the BMI to the paywall](#pass-the-bmi-to-the-paywall) lấy paywall theo ID này.

### Truyền chỉ số BMI vào paywall \{#pass-the-bmi-to-the-paywall\}

Thu thập cân nặng và chiều cao từ các sự kiện `flow_user_input` của flow onboarding, như hướng dẫn trong [Xử lý dữ liệu từ flow](android-flow-input). Để biết người dùng đã đến màn hình cuối cùng, hãy theo dõi sự kiện `flow_screen_showed` có `is_last_screen` được đặt thành `true`, như trong [Theo dõi lượt xem màn hình flow](android-flow-screen-views). Khi người dùng đóng flow onboarding sau màn hình đó, hãy tính BMI. Sau đó tạo flow view của paywall và truyền BMI vào custom tag `bmi`.

Truyền listener sau vào view của flow onboarding:

```kotlin showLineNumbers title="Kotlin"
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))
    }
}
```

When the user closes the onboarding flow after its last screen, the listener calls `onBmiCalculated` with the BMI. Pass a lambda for `onBmiCalculated` that calls the following function. The function fetches the paywall flow, creates its view, and passes the BMI for the `bmi` custom tag:

```kotlin showLineNumbers title="Kotlin"
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` với `Locale.getDefault()` sử dụng dấu phân cách thập phân theo ngôn ngữ của người dùng: "22.7" trong tiếng Anh, "22,7" trong tiếng Đức.

### Lưu BMI vào hồ sơ người dùng \{#save-the-bmi-to-the-users-profile\}

Để hiển thị các flow khác nhau cho người dùng dựa trên BMI của họ, hãy lưu BMI dưới dạng [thuộc tính tùy chỉnh](android-setting-user-attributes#custom-user-attributes) của hồ sơ người dùng. Trong Adapty Dashboard, [tạo một thuộc tính tùy chỉnh kiểu **Number**](segments#custom-attributes) với key `bmi`. Sau đó hiển thị từng flow cho một [đối tượng](audience) được tạo từ thuộc tính đó.

Gọi phương thức `updateProfile` trong `onFlowClosed`, ngay sau dòng tính `bmi`. Truyền vào giá trị `Double` đó, không phải chuỗi đã định dạng, để một phân khúc có thể so sánh BMI với một ngưỡng nhất định:

```kotlin showLineNumbers title="Kotlin"
val params = AdaptyProfileParameters.Builder()
    .withCustomAttribute("bmi", bmi)
    .build()

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