ÿÿ Adapty Flow Builder Quickstart | Tài liệu Adapty

Kích hoạt mua hàng với Flow Builder trong Flutter SDK

Äể kích hoạt in-app purchase, bạn cần hiểu ba khái niệm chính:

  • Sản phẩm – những gì ngưá»i dùng có thể mua (gói đăng ký, consumable, quyá»n truy cập trá»n Ä‘á»i)
  • Flow – chuá»—i màn hình hiển thị sản phẩm cho ngưá»i dùng, được xây dá»±ng bằng Flow Builder không cần code. SDK lấy chúng qua getFlow. Nếu bạn muốn tá»± xây dá»±ng giao diện bằng code, hãy dùng paywall thay thế — xem Triển khai paywall thá»§ công.
  • Placement – vị trí và thá»i Ä‘iểm hiển thị flow trong ứng dụng (ví dụ: main, onboarding, settings). Bạn gắn flow vào placement trên dashboard, sau đó yêu cầu chúng theo placement ID trong code. Cách này giúp bạn dá»… dàng chạy A/B test và hiển thị các flow khác nhau cho từng nhóm ngưá»i dùng. Adapty cung cấp cho bạn ba cách để kích hoạt tính năng mua hàng trong ứng dụng. Hãy chá»n má»™t trong số đó tùy theo yêu cầu cá»§a ứng dụng: | Triển khai | Äá»™ phức tạp | Khi nào nên dùng | |------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Flow Builder | ✅ Dá»… | Bạn tạo má»™t flow hoàn chỉnh, sẵn sàng để mua hàng trong trình tạo không cần code. Adapty tá»± động hiển thị và xá»­ lý toàn bá»™ luồng mua hàng phức tạp, xác thá»±c biên lai và quản lý gói đăng ký. | | Paywall tá»± tạo | 🟡 Trung bình | Bạn tá»± xây dá»±ng giao diện paywall trong code ứng dụng, nhưng vẫn lấy đối tượng flow từ Adapty để duy trì sá»± linh hoạt trong danh sách sản phẩm. Xem hướng dẫn. | | Chế độ Observer | 🔴 Khó | Bạn đã có hạ tầng xá»­ lý mua hàng riêng và muốn tiếp tục dùng nó. Lưu ý rằng chế độ observer có má»™t số hạn chế trong Adapty. Xem bài viết. |

Các bước dưới đây hướng dẫn cách triển khai một flow được tạo trong Adapty Flow Builder.

Nếu bạn muốn tự xây dựng giao diện paywall, hãy xem Triển khai paywall thủ công.

Äể hiển thị má»™t flow được tạo trong Adapty Flow Builder, trong code cá»§a ứng dụng, bạn chỉ cần:

  1. Lấy flow: Lấy flow đó từ Adapty.
  2. Hiển thị flow và Adapty sẽ xử lý các giao dịch mua cho bạn: Hiển thị view trong ứng dụng của bạn.
  3. Xá»­ lý các hành động nút bấm: Liên kết các tương tác cá»§a ngưá»i dùng vá»›i phản hồi tương ứng trong ứng dụng. Ví dụ: mở liên kết hoặc đóng flow khi ngưá»i dùng nhấn nút.

Trước khi bắt đầu

Trước khi bắt đầu, hãy hoàn thành các bước sau:

  1. Kết nối ứng dụng của bạn với App Store và/hoặc Google Play trong Adapty Dashboard.
  2. Tạo sản phẩm trong Adapty.
  3. Tạo flow và thêm sản phẩm vào đó.
  4. Tạo placement và thêm flow vào đó.
  5. Cài đặt và kích hoạt Adapty SDK trong mã ứng dụng của bạn. Hướng dẫn này sử dụng API của Adapty Flutter SDK v4.

Cách nhanh nhất để hoàn thành các bước này là làm theo hướng dẫn quickstart hoặc tạo paywall và placement bằng Developer CLI.

1. Lấy flow

Các flow của bạn được liên kết với các placement được cấu hình trong dashboard. Placement cho phép bạn chạy các flow khác nhau cho các đối tượng khác nhau hoặc để chạy A/B test.

Äể lấy má»™t flow đã tạo trong Adapty Flow Builder, bạn cần:

  1. Lấy đối tượng flow theo ID placement bằng phương thức getFlow và kiểm tra xem nó có được tạo trong builder hay không thông qua thuộc tính hasViewConfiguration.

  2. Tạo flow view bằng phương thức createFlowView. View chứa các phần tử UI và style cần thiết để hiển thị flow.

Äể lấy cấu hình view, bạn phải bật toggle Show on device trong builder. Nếu không, bạn sẽ nhận được cấu hình view rá»—ng và flow sẽ không được hiển thị.


try {
  // the requested flow
  final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
  final view = await AdaptyUI().createFlowView(
    flow: flow,
  );
} on AdaptyError catch (adaptyError) {
  // handle the error
} catch (e) {
  // handle the error
}

2. Hiển thị flow

Bây giá», khi bạn đã có flow view, chỉ cần thêm vài dòng code để hiển thị nó.

Äể hiển thị flow, sá»­ dụng phương thức view.present() trên view được tạo bởi phương thức createFlowView. Má»—i view chỉ có thể được hiển thị má»™t lần: sau khi bạn đóng nó, view sẽ được giải phóng khá»i bá»™ nhá»›. Nếu bạn cần hiển thị lại flow, hãy gá»i createFlowView thêm má»™t lần nữa để tạo má»™t view instance má»›i.

try {
  await view.present();
} on AdaptyError catch (e) {
  // handle the error
} catch (e) {
  // handle the error
}

Äể biết thêm chi tiết vá» cách hiển thị má»™t flow, hãy xem hướng dẫn cá»§a chúng tôi.

3. Xử lý các hành động nút bấm

Khi ngưá»i dùng nhấn các nút trong flow, Flutter SDK tá»± động xá»­ lý các thao tác mua hàng, khôi phục, đóng màn hình và mở URL. Tuy nhiên, má»™t số nút có ID tùy chỉnh hoặc được định sẵn và cần bạn xá»­ lý trong code.

Äể kiểm soát hoặc theo dõi các tiến trình trên màn hình flow, hãy implement các phương thức cá»§a AdaptyUIFlowsEventsObserver và thiết lập observer trước khi hiển thị bất kỳ màn hình nào. Khi ngưá»i dùng thá»±c hiện má»™t hành động, flowViewDidPerformAction sẽ được gá»i, và ứng dụng cá»§a bạn cần phản hồi tương ứng vá»›i action ID đó. Có ba phương thức observer bắt buá»™c: flowViewDidFinishPurchase, flowViewDidFinishRestore, và flowViewDidReceiveError — class cá»§a bạn sẽ không thể biên dịch nếu thiếu chúng.

Äá»c hướng dẫn cá»§a chúng tôi vá» cách xá»­ lý actions và events cá»§a nút bấm.

Triển khai observer dưới dạng một đối tượng độc lập, tồn tại lâu dài thay vì gắn vào widget. Vì toàn bộ ứng dụng chỉ dùng chung một slot observer duy nhất, nếu gắn vào State sẽ gây rò rỉ màn hình (SDK giữ một strong reference đến nó) và sẽ bị thay thế âm thầm khi màn hình tiếp theo đăng ký. Sử dụng extends cũng kế thừa hành vi mặc định của SDK, vì vậy ngoài ba phương thức bắt buộc, bạn chỉ cần override những callback mà bạn quan tâm.

// A dedicated, long-lived handler for flow events.
// It does NOT live inside a Widget/State, so it never leaks and is never
// silently replaced when screens are pushed or popped.
class FlowEventsHandler extends AdaptyUIFlowsEventsObserver {
  // A single, app-wide instance — same idiom as Adapty() and AdaptyUI().
  static final FlowEventsHandler _instance = FlowEventsHandler._();
  factory FlowEventsHandler() => _instance;
  FlowEventsHandler._();

  // This method is called when user performs an action on the flow UI.
  // Overriding it replaces the default behavior (dismiss on close, open URLs),
  // so keep those cases if you want to preserve it.
  @override
  void flowViewDidPerformAction(AdaptyUIFlowView view, AdaptyUIAction action) {
    switch (action) {
      case const CloseAction():
      case const AndroidSystemBackAction(): // close the flow on the Android back button
        view.dismiss();
        break;
      case OpenUrlAction(:final url, :final openIn):
        AdaptyUI().openUrl(url, openIn: openIn);
        break;
      default:
        break;
    }
  }

  // Required: decide what happens after a purchase finishes
  @override
  void flowViewDidFinishPurchase(AdaptyUIFlowView view,
      AdaptyPaywallProduct product, AdaptyPurchaseResult purchaseResult) {
    if (purchaseResult is! AdaptyPurchaseResultUserCancelled) {
      view.dismiss();
    }
  }

  // Required: dismiss the flow once a restore succeeds
  @override
  void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
    view.dismiss();
  }

  // Required: handle rendering and other view errors
  @override
  void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
    print('Flow error: $error');
    view.dismiss();
  }
}

Äăng ký handler má»™t lần khi khởi động ứng dụng, trước khi hiển thị bất kỳ flow nào:

AdaptyUI().setFlowsEventsObserver(FlowEventsHandler());

Bước tiếp theo

Bạn có câu há»i hoặc gặp sá»± cố? Hãy xem diá»…n đàn há»— trợ cá»§a chúng tôi — nÆ¡i bạn có thể tìm câu trả lá»i cho các câu há»i thưá»ng gặp hoặc đặt câu há»i cá»§a riêng mình. Äá»™i ngÅ© và cá»™ng đồng cá»§a chúng tôi luôn sẵn sàng giúp đỡ!

Paywall của bạn đã sẵn sàng để hiển thị trong ứng dụng. Hãy kiểm tra mua hàng trong sandbox App Store hoặc Google Play Store để đảm bảo bạn có thể hoàn thành một lần mua thử từ paywall.

Tiếp theo, bạn cần kiểm tra mức độ truy cập cá»§a ngưá»i dùng để đảm bảo bạn hiển thị paywall hoặc cấp quyá»n truy cập vào các tính năng trả phí cho đúng ngưá»i dùng.

Ví dụ đầy đủ

Dưới đây là cách tích hợp tất cả các bước trên vào ứng dụng của bạn.


void main() {
  // Register a single, long-lived observer once, before any flow is shown.
  // It is intentionally a plain object (NOT a Widget/State): its lifetime is the
  // whole app, so it never leaks and is never silently replaced when screens are
  // pushed or popped.
  AdaptyUI().setFlowsEventsObserver(FlowEventsHandler());

  runApp(MaterialApp(home: FlowScreen()));
}

/// A dedicated handler for AdaptyUI flow events.
///
/// It `extends` [AdaptyUIFlowsEventsObserver] (rather than being implemented
/// by a `State`), which gives you two things for free:
///   * the SDK's sensible defaults for optional callbacks, so besides the three
///     required methods you only override what you actually care about;
///   * a lifecycle that is independent of the widget tree — there is no strong
///     reference back into a `Widget`, so nothing leaks and there is nothing to
///     unregister.
///
/// Every callback receives the [AdaptyUIFlowView] it relates to, so handling
/// flow actions never requires a `BuildContext` or widget state.
class FlowEventsHandler extends AdaptyUIFlowsEventsObserver {
  // A single, app-wide instance — same idiom as Adapty() and AdaptyUI().
  static final FlowEventsHandler _instance = FlowEventsHandler._();
  factory FlowEventsHandler() => _instance;
  FlowEventsHandler._();

  // Called when the user performs an action on the flow UI.
  @override
  void flowViewDidPerformAction(AdaptyUIFlowView view, AdaptyUIAction action) {
    switch (action) {
      case const CloseAction():
      case const AndroidSystemBackAction(): // close the flow on the Android back button
        view.dismiss();
        break;
      case OpenUrlAction(:final url, :final openIn):
        // Open the URL natively, honoring the dashboard browser setting.
        AdaptyUI().openUrl(url, openIn: openIn);
        break;
      default:
        break;
    }
  }

  // Required: decide what happens after a purchase finishes.
  @override
  void flowViewDidFinishPurchase(AdaptyUIFlowView view,
      AdaptyPaywallProduct product, AdaptyPurchaseResult purchaseResult) {
    if (purchaseResult is! AdaptyPurchaseResultUserCancelled) {
      view.dismiss();
    }
  }

  // Required: dismiss the flow once a restore succeeds.
  @override
  void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
    view.dismiss();
  }

  // Required: handle rendering and other view errors.
  @override
  void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
    print('Flow error: $error');
    view.dismiss();
  }
}

class FlowScreen extends StatefulWidget {
  const FlowScreen({super.key});

  @override
  State<FlowScreen> createState() => _FlowScreenState();
}

class _FlowScreenState extends State<FlowScreen> {
  @override
  void initState() {
    super.initState();
    _showFlowIfNeeded();
  }

  Future<void> _showFlowIfNeeded() async {
    try {
      final flow = await Adapty().getFlow(
        placementId: 'YOUR_PLACEMENT_ID',
      );

      if (!flow.hasViewConfiguration) return;

      final view = await AdaptyUI().createFlowView(flow: flow);

      await view.present();
    } catch (_) {
      // Handle any errors (network, SDK issues, etc.)
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Adapty Flow Example')),
      body: Center(
        // Add a button to re-trigger the flow for testing purposes.
        child: ElevatedButton(
          onPressed: _showFlowIfNeeded,
          child: const Text('Show Flow'),
        ),
      ),
    );
  }
}
ÿÿÿÿ