---
title: "Lấy paywall và cấu hình của chúng bằng Paywall Builder trong Flutter SDK"
description: "Tìm hiểu cách lấy paywall PB trong Adapty để kiểm soát gói đăng ký tốt hơn trong Flutter."
---

Sau khi [bạn đã thiết kế phần giao diện cho paywall](adapty-paywall-builder) bằng Paywall Builder mới trong Adapty Dashboard, bạn có thể hiển thị nó trong ứng dụng di động. Bước đầu tiên là lấy paywall gắn với placement và cấu hình view của nó như mô tả bên dưới.

:::warning
Paywall Builder mới yêu cầu Flutter SDK phiên bản 3.3.0 trở lên.
:::

Lưu ý rằng chủ đề này đề cập đến các paywall được tùy chỉnh bằng Paywall Builder. Nếu bạn đang tự triển khai paywall theo cách thủ công, hãy tham khảo chủ đề [Lấy paywall và sản phẩm cho paywall remote config trong ứng dụng di động](fetch-paywalls-and-products-flutter).

:::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 hiển thị paywall trong ứng dụng di động (nhấn để 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 đó](create-paywall) trong Adapty Dashboard.
3. [Tạo placement và thêm paywall vào đó](create-placement) trong Adapty Dashboard.
4. Cài đặt [Adapty SDK](sdk-installation-flutter) trong ứng dụng di động của bạn.
</details>

## Lấy paywall được thiết kế bằng Paywall Builder \{#fetch-paywall-designed-with-paywall-builder\}

Nếu bạn đã [thiết kế paywall bằng Paywall Builder](adapty-paywall-builder), bạn không cần lo lắng về việc render nó trong code ứng dụng để hiển thị cho người dùng. Paywall như vậy chứa cả nội dung hiển thị lẫn cách thức hiển thị. Tuy nhiên, bạn vẫn cần lấy ID của nó thông qua placement, cấu hình view của nó, rồi hiển thị trong ứng dụng di động.

Để đảm bảo hiệu suất tối ưu, hãy lấy paywall và [cấu hình view](flutter-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder) của nó càng sớm càng tốt, để ảnh có đủ thời gian tải xuống trước khi hiển thị cho người dùng.

Để lấy paywall, dùng phương thức `getPaywall`:

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

Tham số:

| Tham số | Bắt buộc | Mô tả |
|---------|--------|-----------|
| **placementId** | bắt buộc | Định danh của [Placement](placements) mong muốn. Đâ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-paywall-locale-in-adapty-paywall-builder). Tham số này là mã ngôn ngữ gồm một hoặc hai thẻ phụ được phân cách bằng dấu trừ (**-**). Thẻ phụ đầu tiên là ngôn ngữ, thẻ thứ hai là vùng.</p><p></p><p>Ví dụ: `en` là tiếng Anh, `pt-br` là tiếng Bồ Đào Nha (Brazil).</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 nếu 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ó 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 thời gian tải sẽ nhanh hơn dù kết nối có kém đến đâu. Cache được cập nhật thường xuyên nên an toàn khi dùng trong phiên để tránh các yêu cầu mạng.</p><p></p><p>Lưu ý rằng cache vẫn tồn tại khi khởi động lại ứng dụng và chỉ bị xóa khi cài đặt lại ứng dụng hoặc xóa thủ công.</p><p></p><p>Adapty SDK lưu trữ paywall cục bộ theo hai lớp: cache được cập nhật thường xuyên như mô tả ở trên và [paywall dự phòng](fallback-paywalls). Chúng tôi cũng dùng CDN để tải paywall nhanh hơn và máy chủ dự phòng độc lập trong trường hợp CDN không tiếp cận được. 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 paywall trong khi vẫn đảm bảo độ tin cậy ngay cả khi kết nối internet kém.</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>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 `loadTimeout` đã chỉ định, vì thao tác có thể bao gồm nhiều yêu cầu bên dưới.</p><p>Đối với Android: Bạn có thể tạo `TimeInterval` bằng các hàm mở rộng (như `5.seconds`, trong đó `.seconds` đến từ `import com.adapty.utils.seconds`), hoặc `TimeInterval.seconds(5)`. Để không giới hạn thời gian, dùng `TimeInterval.INFINITE`.</p> |

Tham số phản hồi:

| Tham số | Mô tả |
| :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Paywall   | Một đối tượng [`AdaptyPaywall`](https://pub.dev/documentation/adapty_flutter/latest/adapty_flutter/AdaptyPaywall-class.html) với 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 cấu hình view của paywall được thiết kế bằng Paywall Builder \{#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder\}

:::important
Hãy đảm bảo bật nút **Show on device** trong paywall builder. Nếu tùy chọn này chưa được bật, cấu hình view sẽ không thể lấy được.
:::

Sau khi lấy paywall, hãy kiểm tra xem nó có chứa `ViewConfiguration` hay không — điều này cho biết paywall được tạo bằng Paywall Builder. Thông tin này sẽ hướng dẫn bạn cách hiển thị paywall. Nếu có `ViewConfiguration`, xử lý nó như paywall Paywall Builder; nếu không, [xử lý nó như paywall remote config](present-remote-config-paywalls-flutter).

```dart showLineNumbers

try {
  final view = await AdaptyUI().createPaywallView(
        paywall: paywall,
      );
} on AdaptyError catch (e) {
  // handle the error
} catch (e) {
  // handle the error
}
```

Sau khi có view, [hiển thị paywall](flutter-present-paywalls).

## Lấy paywall cho đối tượng mặc định để tải nhanh hơn \{#get-a-paywall-for-a-default-audience-to-fetch-it-faster\}

Thông thường, paywall đượ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à paywall, và người dùng 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ị 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 điều này, bạn có thể 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ư đã mô tả trong phần [Lấy thông tin paywall](flutter-get-pb-paywalls#fetch-paywall-designed-with-paywall-builder) ở 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ể:

- **Vấn đề tương thích ngược tiềm ẩn**: 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ế paywall hỗ trợ phiên bản hiện tại (cũ) hoặc chấp nhận rằng người dùng dùng phiên bản hiện tại (cũ) có thể gặp sự cố với paywall không render được.
- **Mất khả năng targeting**: 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 đi khả năng targeting 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 để được hưởng lợi từ việc lấy paywall nhanh hơn, dùng phương thức `getPaywallForDefaultAudience` như sau. Ngược lại, hãy dùng `getPaywall` như mô tả [ở trên](#fetch-paywall-designed-with-paywall-builder).
:::

```dart showLineNumbers
try {
    final paywall = await Adapty().getPaywallForDefaultAudience(placementId: 'YOUR_PLACEMENT_ID');
} on AdaptyError catch (adaptyError) {
    // handle error
} catch (e) {
    // handle unknown error
}
```

:::note
Phương thức `getPaywallForDefaultAudience` có sẵn từ Flutter SDK phiên bản 3.2.0 trở lên.
:::

| 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 là mã ngôn ngữ gồm một hoặc nhiều thẻ phụ được phân cách bằng dấu trừ (**-**). Thẻ phụ đầu tiên là ngôn ngữ, thẻ thứ hai là vùng.</p><p></p><p>Ví dụ: `en` là tiếng Anh, `pt-br` là tiếng Bồ Đào Nha (Brazil).</p><p></p><p>Xem [Bản địa hóa và mã locale](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 nếu 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ó 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 thời gian tải sẽ nhanh hơn dù kết nối có kém đến đâu. Cache được cập nhật thường xuyên nên an toàn khi dùng trong phiên để tránh các yêu cầu mạng.</p><p></p><p>Lưu ý rằng cache vẫn tồn tại khi khởi động lại ứng dụng và chỉ bị xóa khi cài đặt lại ứng dụng hoặc xóa thủ công.</p> |

## Tùy chỉnh assets \{#customize-assets\}

Để tùy chỉnh hình ảnh và video trong paywall, hãy triển khai custom assets.

Hình ảnh hero và video có ID được xác định trước: `hero_image` và `hero_video`. Trong một custom asset bundle, bạn nhắm đến các phần tử này bằng ID của chúng và tùy chỉnh hành vi của chúng.

Đối với các hình ảnh và video khác, bạn cần [đặt custom ID](custom-media) trong Adapty dashboard.

Ví dụ, bạn có thể:

- Hiển thị hình ảnh hoặc video khác cho một số người dùng.
- Hiển thị hình ảnh preview cục bộ trong khi hình ảnh chính từ xa đang tải.
- Hiển thị hình ảnh preview trước khi chạy video.

:::important
Để sử dụng tính năng này, hãy cập nhật Adapty Flutter SDK lên phiên bản 3.8.0 trở lên.
:::

Đây là ví dụ về cách cung cấp custom assets thông qua một dictionary đơn giản:

```dart

final customAssets = {
    // Show a local image using a custom ID
    'custom_image': AdaptyCustomAsset.localImageAsset(
        assetId: 'assets/images/image_name.png',
    ),

    // Show a local video with a preview image
    'hero_video': AdaptyCustomAsset.localVideoAsset(
        assetId: 'assets/videos/custom_video.mp4',
    ),
};

try {
    final view = await AdaptyUI().createPaywallView(
        paywall: paywall,
        customAssets: <CUSTOM_ASSETS>,
        preloadProducts: preloadProducts,
        );
    } on AdaptyError catch (e) {
        // handle the error
    } catch (e) {
// handle the error
}
```

:::note
Nếu không tìm thấy asset, paywall sẽ hiển thị theo giao diện mặc định.
:::

## Thiết lập timer do nhà phát triển định nghĩa \{#set-up-developer-defined-timers\}

Để sử dụng custom timer trong ứng dụng di động, hãy tạo một đối tượng tuân theo giao thức `AdaptyTimerResolver`. Đối tượng này định nghĩa cách mỗi custom timer sẽ được render. Nếu muốn, bạn có thể dùng trực tiếp dictionary `[String: Date]` vì nó đã tuân thủ giao thức này. Đây là ví dụ:

```dart showLineNumbers

try {
  final view = await AdaptyUI().createPaywallView(
        paywall: paywall,
        customTimers: {
          'CUSTOM_TIMER_6H': DateTime.now().add(const Duration(seconds: 3600 * 6)),
          'CUSTOM_TIMER_NY': DateTime(2025, 1, 1), // New Year 2025
        },
      );
} on AdaptyError catch (e) {
  // handle the error
} catch (e) {
  // handle the error
}
```

Trong ví dụ này, `CUSTOM_TIMER_NY` và `CUSTOM_TIMER_6H` là **Timer ID** của các timer do nhà phát triển định nghĩa mà bạn đã thiết lập trong Adapty Dashboard. `timerResolver` đảm bảo ứng dụng của bạn cập nhật động mỗi timer với giá trị chính xác. Ví dụ:

- `CUSTOM_TIMER_NY`: Thời gian còn lại cho đến khi timer kết thúc, chẳng hạn như Năm mới.
- `CUSTOM_TIMER_6H`: Thời gian còn lại trong khoảng thời gian 6 giờ bắt đầu khi người dùng mở paywall.