---
title: "Lấy paywalls và sản phẩm cho Remote Config paywalls trong Flutter SDK"
description: "Lấy paywalls và sản phẩm trong Adapty Flutter SDK để tăng cường khả năng kiếm tiền từ người dùng."
---

Trước khi hiển thị Remote Config và các paywall tùy chỉnh, bạn cần lấy thông tin về chúng. Lưu ý rằng chủ đề này đề cập đến Remote Config và các paywall tùy chỉnh. Để biết hướng dẫn lấy paywalls cho Paywall Builder, vui lòng tham khảo [Lấy Paywall Builder paywalls và cấu hình của chúng](flutter-get-pb-paywalls).

:::tip

Muốn xem ví dụ thực tế về cách tích hợp Adapty SDK vào ứng dụng di động? Hãy xem [ứng dụng mẫu](sample-apps) của chúng tôi, nơi minh họa toàn bộ quá trình thiết lập, bao gồm hiển thị paywall, thực hiện mua hàng và các chức năng cơ bản khác.

:::

<details>
   <summary>Trước khi bắt đầu lấy paywalls và sản phẩm trong ứng dụng của bạn (click để mở rộng)</summary>

   1. [Tạo sản phẩm](create-product) trong Adapty Dashboard.

2. [Tạo paywall và thêm sản phẩm vào paywall](create-paywall) trong Adapty Dashboard.

3. [Tạo placement và thêm paywall vào placement](create-placement) trong Adapty Dashboard.

4. [Cài đặt Adapty SDK](sdk-installation-flutter) trong ứng dụng của bạn.
</details>

## Lấy thông tin paywall \{#fetch-paywall-information\}

Trong Adapty, một [sản phẩm](product) là sự kết hợp của các sản phẩm từ cả App Store và Google Play. Các sản phẩm đa nền tảng này được tích hợp vào paywalls, cho phép bạn hiển thị chúng trong các placement cụ thể của ứng dụng.

Để hiển thị sản phẩm, bạn cần lấy một [Paywall](paywalls) từ một trong các [placement](placements) của bạn bằng phương thức `getPaywall`.

:::important
**Đừng hardcode ID sản phẩm.** ID duy nhất bạn nên hardcode là placement ID. Paywalls được cấu hình từ xa, vì vậy số lượng sản phẩm và các ưu đãi có thể thay đổi bất kỳ lúc nào. Ứng dụng của bạn phải xử lý những thay đổi này một cách linh hoạt — nếu hôm nay một paywall trả về hai sản phẩm và ngày mai trả về ba, hãy hiển thị tất cả mà không cần thay đổi code.
:::

```dart showLineNumbers
try {
  final paywall = await Adapty().getPaywall(id: "YOUR_PLACEMENT_ID", locale: "en");
  // the requested paywall
} on AdaptyError catch (adaptyError) {
  // handle the error
} catch (e) {
}
```

| Tham số | Bắt buộc | Mô tả |
|---------|--------|-----------|
| **placementId** | bắt buộc | Định danh của [Placement](placements). Đây là giá trị bạn đã chỉ định khi tạo placement trong Adapty Dashboard. |
| **locale** | <p>tùy chọn</p><p>mặc định: `en`</p> | <p>Định danh của [bản địa hóa paywall](add-remote-config-locale). Tham số này được kỳ vọng là một mã ngôn ngữ gồm một hoặc nhiều subtag được phân cách bằng dấu trừ (**-**). Subtag đầu tiên dành cho ngôn ngữ, subtag thứ hai dành cho vùng.</p><p></p><p>Ví dụ: `en` nghĩa là tiếng Anh, `pt-br` đại diện cho tiếng Bồ Đào Nha của Brazil.</p><p></p><p>Xem [Bản địa hóa và mã locale](flutter-localizations-and-locale-codes) để biết thêm thông tin về mã locale và cách chúng tôi khuyến nghị sử dụng chúng.</p> |
| **fetchPolicy** | mặc định: `.reloadRevalidatingCacheData` | <p>Theo mặc định, SDK sẽ cố tải dữ liệu từ máy chủ và trả về dữ liệu đã cache trong trường hợp thất bại. Chúng tôi khuyến nghị tùy chọn này vì nó đảm bảo người dùng luôn nhận được dữ liệu mới nhất.</p><p></p><p>Tuy nhiên, nếu bạn cho rằng người dùng của mình có kết nối internet không ổn định, hãy cân nhắc dùng `.returnCacheDataElseLoad` để trả về dữ liệu đã cache nếu có. Trong trường hợp này, người dùng có thể không nhận được dữ liệu mới nhất tuyệt đối, nhưng sẽ có trải nghiệm tải nhanh hơn, dù kết nối internet của họ có chập chờn thế nào. Cache được cập nhật thường xuyên, nên sử dụng trong phiên để tránh các yêu cầu mạng là hoàn toàn an toàn.</p><p></p><p>Lưu ý rằng cache vẫn còn nguyên sau khi khởi động lại ứng dụng và chỉ bị xóa khi ứng dụng được cài đặt lại hoặc xóa thủ công.</p><p></p><p>Adapty SDK lưu trữ paywalls ở hai lớp: cache được cập nhật thường xuyên như mô tả ở trên và [paywall dự phòng](flutter-use-fallback-paywalls). Chúng tôi cũng sử dụng CDN để lấy paywalls nhanh hơn và một máy chủ dự phòng độc lập trong trường hợp CDN không thể truy cập. Hệ thống này được thiết kế để đảm bảo bạn luôn nhận được phiên bản mới nhất của paywalls, đồng thời đảm bảo độ tin cậy ngay cả khi kết nối internet yếu.</p> |
| **loadTimeout** | mặc định: 5 giây | <p>Giá trị này giới hạn thời gian chờ cho phương thức này. Nếu hết thời gian chờ, dữ liệu đã cache hoặc fallback cục bộ sẽ được trả về.</p><p></p><p>Lưu ý rằng trong một số trường hợp hiếm gặp, phương thức này có thể hết thời gian chờ muộn hơn một chút so với giá trị đã chỉ định trong `loadTimeout`, vì thao tác có thể bao gồm nhiều yêu cầu khác nhau bên dưới.</p> |

Đừng hardcode ID sản phẩm! Vì paywalls được cấu hình từ xa, các sản phẩm có sẵn, số lượng sản phẩm và các ưu đãi đặc biệt (như dùng thử miễn phí) có thể thay đổi theo thời gian. Hãy đảm bảo code của bạn xử lý được các tình huống này.  
Ví dụ, nếu ban đầu bạn lấy được 2 sản phẩm, ứng dụng nên hiển thị 2 sản phẩm đó. Tuy nhiên, nếu sau này bạn lấy được 3 sản phẩm, ứng dụng nên hiển thị cả 3 mà không cần thay đổi code. Thứ duy nhất bạn phải hardcode là placement ID.

Tham số trả về:

| Tham số | Mô tả                                                                                                                                                  |
| :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Paywall   | Một đối tượng [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) gồm: danh sách ID sản phẩm, định danh paywall, Remote Config, và một số thuộc tính khác. |

## Lấy sản phẩm \{#fetch-products\}

Sau khi có paywall, bạn có thể truy vấn mảng sản phẩm tương ứng với nó:

```dart showLineNumbers
try {
  final products = await Adapty().getPaywallProducts(paywall: paywall);
  // the requested products array
} on AdaptyError catch (adaptyError) {
  // handle the error
} catch (e) {
}
```

Tham số trả về:

| Tham số | Mô tả                                                                                                                                                                                 |
| :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Products  | Danh sách các đối tượng [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html) gồm: định danh sản phẩm, tên sản phẩm, giá, tiền tệ, thời hạn gói đăng ký, và một số thuộc tính khác. |

Khi tự thiết kế giao diện paywall, bạn thường cần truy cập các thuộc tính này từ đối tượng [`AdaptyPaywallProduct`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywallProduct-class.html). Dưới đây là các thuộc tính được sử dụng phổ biến nhất, nhưng hãy tham khảo tài liệu được liên kết để biết đầy đủ thông tin về tất cả các thuộc tính có sẵn.

| Thuộc tính                | Mô tả                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Title**               | Để hiển thị tiêu đề sản phẩm, dùng `product.localizedTitle`. Lưu ý rằng bản địa hóa dựa trên quốc gia cửa hàng mà người dùng đã chọn, chứ không phải locale của thiết bị.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| **Price**               | Để hiển thị giá đã được bản địa hóa, dùng `product.price.localizedString`. Bản địa hóa này dựa trên thông tin locale của thiết bị. Bạn cũng có thể truy cập giá dưới dạng số bằng `product.price.amount`. Giá trị sẽ được cung cấp theo đơn vị tiền tệ địa phương. Để lấy ký hiệu tiền tệ tương ứng, dùng `product.price.currencySymbol`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| **Subscription Period** | Để hiển thị chu kỳ (ví dụ: tuần, tháng, năm, v.v.), dùng `product.subscription?.localizedPeriod`. Bản địa hóa này dựa trên locale của thiết bị. Để lấy chu kỳ gói đăng ký theo chương trình, dùng `product.subscription?.period`. Từ đó bạn có thể truy cập enum `unit` để lấy độ dài (tức là ngày, tuần, tháng, năm, hoặc không xác định). Giá trị `numberOfUnits` sẽ cho bạn biết số đơn vị chu kỳ. Ví dụ, đối với gói đăng ký theo quý, bạn sẽ thấy `AdaptyPeriodUnit.month` trong thuộc tính unit và `3` trong thuộc tính numberOfUnits.                                                                                                                                                                                                                                                                                                       |
| **Introductory Offer**  | Để hiển thị huy hiệu hoặc chỉ báo khác rằng gói đăng ký có ưu đãi giới thiệu, hãy xem thuộc tính `product.subscription?.offer?.phases`. Đây là một danh sách có thể chứa tối đa hai giai đoạn giảm giá: giai đoạn dùng thử miễn phí và giai đoạn giá ưu đãi. Trong mỗi đối tượng giai đoạn có các thuộc tính hữu ích sau:<br/>• `paymentMode`: một enum với các giá trị `AdaptyPaymentMode.freeTrial`, `AdaptyPaymentMode.payAsYouGo`, `AdaptyPaymentMode.payUpFront`, và `AdaptyPaymentMode.unknown`. Dùng thử miễn phí sẽ thuộc loại `AdaptyPaymentMode.freeTrial`.<br/>• `price`: Giá giảm dưới dạng số. Đối với dùng thử miễn phí, tìm giá trị `0` ở đây.<br/>• `localizedNumberOfPeriods`: một chuỗi được bản địa hóa theo locale của thiết bị, mô tả độ dài của ưu đãi. Ví dụ, ưu đãi dùng thử ba ngày sẽ hiển thị `3 days` trong trường này.<br/>• `subscriptionPeriod`: Ngoài ra, bạn có thể lấy thông tin chi tiết riêng lẻ về chu kỳ ưu đãi bằng thuộc tính này. Nó hoạt động theo cách tương tự cho các ưu đãi như phần trước đã mô tả.<br/>• `localizedSubscriptionPeriod`: Chu kỳ gói đăng ký được định dạng theo locale của người dùng cho phần giảm giá. |

## Tăng tốc lấy paywall với paywall đối tượng mặc định \{#speed-up-paywall-fetching-with-default-audience-paywall\}

Thông thường, paywalls được lấy gần như ngay lập tức, nên bạn không cần lo lắng về việc tăng tốc quá trình này. Tuy nhiên, trong trường hợp bạn có nhiều đối tượng và paywalls, và người dùng của bạn có kết nối internet yếu, việc lấy paywall có thể mất nhiều thời gian hơn mong muốn. Trong những tình huống như vậy, bạn có thể muốn hiển thị một paywall mặc định để đảm bảo trải nghiệm người dùng mượt mà thay vì không hiển thị paywall nào.

Để giải quyết vấn đề này, bạn có thể sử dụng phương thức `getPaywallForDefaultAudience`, phương thức này lấy paywall của placement được chỉ định cho đối tượng **All Users**. Tuy nhiên, điều quan trọng cần hiểu là cách tiếp cận được khuyến nghị là lấy paywall bằng phương thức `getPaywall`, như được mô tả chi tiết trong phần [Lấy thông tin paywall](fetch-paywalls-and-products-flutter#fetch-paywall-information) ở trên.

:::warning
Lý do chúng tôi khuyến nghị dùng `getPaywall`

Phương thức `getPaywallForDefaultAudience` có một số hạn chế đáng kể:

- **Có thể gặp vấn đề tương thích ngược**: Nếu bạn cần hiển thị các paywall khác nhau cho các phiên bản ứng dụng khác nhau (hiện tại và tương lai), bạn có thể gặp khó khăn. Bạn sẽ phải thiết kế paywalls hỗ trợ phiên bản hiện tại (cũ) hoặc chấp nhận rằng người dùng với phiên bản hiện tại (cũ) có thể gặp sự cố với các paywalls không được render.
- **Mất khả năng nhắm mục tiêu**: Tất cả người dùng sẽ thấy cùng một paywall được thiết kế cho đối tượng **All Users**, nghĩa là bạn mất nhắm mục tiêu cá nhân hóa (bao gồm theo quốc gia, attribution marketing hoặc các thuộc tính tùy chỉnh của bạn).

Nếu bạn sẵn sàng chấp nhận những hạn chế này để hưởng lợi từ việc lấy paywall nhanh hơn, hãy sử dụng phương thức `getPaywallForDefaultAudience` như mô tả dưới đây. Nếu không, hãy tiếp tục dùng `getPaywall` như đã mô tả [ở trên](fetch-paywalls-and-products-flutter#fetch-paywall-information).
:::

:::note
Phương thức `getPaywallForDefaultAudience` chưa được hỗ trợ trong Flutter SDK, nhưng sẽ sớm được thêm vào.
:::

| Tham số | Bắt buộc | Mô tả |
|---------|--------|-----------|
| **placementId** | bắt buộc | Định danh của [Placement](placements). Đây là giá trị bạn đã chỉ định khi tạo placement trong Adapty Dashboard. |
| **locale** | <p>tùy chọn</p><p>mặc định: `en`</p> | <p>Định danh của [bản địa hóa paywall](add-remote-config-locale). Tham số này được kỳ vọng là một mã ngôn ngữ gồm một hoặc nhiều subtag được phân cách bằng dấu trừ (**-**). Subtag đầu tiên dành cho ngôn ngữ, subtag thứ hai dành cho vùng.</p><p></p><p>Ví dụ: `en` nghĩa là tiếng Anh, `pt-br` đại diện cho tiếng Bồ Đào Nha của Brazil.</p><p></p><p>Xem [Bản địa hóa và mã locale](flutter-localizations-and-locale-codes) để biết thêm thông tin về mã locale và cách chúng tôi khuyến nghị sử dụng chúng.</p> |
| **fetchPolicy** | mặc định: `.reloadRevalidatingCacheData` | <p>Theo mặc định, SDK sẽ cố tải dữ liệu từ máy chủ và trả về dữ liệu đã cache trong trường hợp thất bại. Chúng tôi khuyến nghị tùy chọn này vì nó đảm bảo người dùng luôn nhận được dữ liệu mới nhất.</p><p></p><p>Tuy nhiên, nếu bạn cho rằng người dùng của mình có kết nối internet không ổn định, hãy cân nhắc dùng `.returnCacheDataElseLoad` để trả về dữ liệu đã cache nếu có. Trong trường hợp này, người dùng có thể không nhận được dữ liệu mới nhất tuyệt đối, nhưng sẽ có trải nghiệm tải nhanh hơn, dù kết nối internet của họ có chập chờn thế nào. Cache được cập nhật thường xuyên, nên sử dụng trong phiên để tránh các yêu cầu mạng là hoàn toàn an toàn.</p><p></p><p>Lưu ý rằng cache vẫn còn nguyên sau khi khởi động lại ứng dụng và chỉ bị xóa khi ứng dụng được cài đặt lại hoặc xóa thủ công.</p> |