---
title: "Xử lý sự kiện onboarding trong Flutter SDK"
description: "Xử lý các sự kiện liên quan đến onboarding trong Flutter sử dụng Adapty."
---

Các onboarding được cấu hình bằng builder sẽ tạo ra các sự kiện mà ứng dụng của bạn có thể xử lý. Cách xử lý các sự kiện này phụ thuộc vào phương thức hiển thị bạn đang sử dụng:

- **Hiển thị toàn màn hình**: Yêu cầu thiết lập một global event observer để xử lý sự kiện cho tất cả các onboarding view
- **Widget nhúng**: Xử lý sự kiện thông qua các tham số callback inline trực tiếp trong widget

Trước khi bắt đầu, hãy đảm bảo rằng:

1. Bạn đã cài đặt [Adapty Flutter SDK](sdk-installation-flutter) phiên bản 3.8.0 trở lên.
2. Bạn đã [tạo một onboarding](create-onboarding).
3. Bạn đã thêm onboarding vào một [placement](placements).

## Sự kiện hiển thị toàn màn hình \{#full-screen-presentation-events\}

### Thiết lập event observer \{#set-up-event-observer\}

Để xử lý sự kiện cho các onboarding toàn màn hình, hãy triển khai `AdaptyUIOnboardingsEventsObserver` và thiết lập nó trước khi hiển thị:

```javascript showLineNumbers title="Flutter"
AdaptyUI().setOnboardingsEventsObserver(this);

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

### Xử lý sự kiện \{#handle-events\}

Triển khai các phương thức sau trong observer của bạn:

```javascript showLineNumbers title="Flutter"
void onboardingViewDidFinishLoading(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
) {
  // Onboarding finished loading
}

void onboardingViewDidFailWithError(
  AdaptyUIOnboardingView view,
  AdaptyError error,
) {
  // Handle loading errors
}

void onboardingViewOnCloseAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // Handle close action
  view.dismiss();
}

void onboardingViewOnPaywallAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // Dismiss onboarding before presenting paywall
  view.dismiss().then((_) {
    _openPaywall(actionId);
  });
}

void onboardingViewOnCustomAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // Handle custom actions
}

void onboardingViewOnStateUpdatedAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String elementId,
  AdaptyOnboardingsStateUpdatedParams params,
) {
  // Handle user input updates
}

void onboardingViewOnAnalyticsEvent(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  AdaptyOnboardingsAnalyticsEvent event,
) {
  // Track analytics events
}
```

## Sự kiện widget nhúng \{#embedded-widget-events\}

Khi sử dụng `AdaptyUIOnboardingPlatformView`, bạn có thể xử lý sự kiện thông qua các tham số callback inline trực tiếp trong widget. Lưu ý rằng sự kiện sẽ được gửi đến cả callback của widget và global observer (nếu đã thiết lập), nhưng global observer là tùy chọn:

```javascript showLineNumbers title="Flutter"
AdaptyUIOnboardingPlatformView(
  onboarding: onboarding,
  onDidFinishLoading: (meta) {
    // Onboarding finished loading
  },
  onDidFailWithError: (error) {
    // Handle loading errors
  },
  onCloseAction: (meta, actionId) {
    // Handle close action
  },
  onPaywallAction: (meta, actionId) {
    _openPaywall(actionId);
  },
  onCustomAction: (meta, actionId) {
    // Handle custom actions
  },
  onStateUpdatedAction: (meta, elementId, params) {
    // Handle user input updates
  },
  onAnalyticsEvent: (meta, event) {
    // Track analytics events
  },
)
```

## Các loại sự kiện \{#event-types\}

Các phần sau mô tả các loại sự kiện khác nhau mà bạn có thể xử lý, bất kể phương thức hiển thị nào bạn đang sử dụng.

### Xử lý custom action \{#handle-custom-actions\}

Trong builder, bạn có thể thêm hành động **custom** vào một nút và gán ID cho nó.

  <img src="/assets/shared/img/ios-events-1.webp"
  style={{
    border: '1px solid #727272', /* border width and color */
    width: '700px', /* image width */
    display: 'block', /* for alignment */
    margin: '0 auto' /* center alignment */
  }}
/>

Sau đó, bạn có thể sử dụng ID này trong code và xử lý nó như một custom action. Ví dụ: nếu người dùng nhấn vào một nút tùy chỉnh như **Login** hoặc **Allow notifications**, phương thức delegate `onboardingController` sẽ được kích hoạt với case `.custom(id:)` và tham số `actionId` chính là **Action ID** từ builder. Bạn có thể tạo ID riêng của mình, như "allowNotifications".

```javascript
// Full-screen presentation
void onboardingViewOnCustomAction(
    AdaptyUIOnboardingView view,
    AdaptyUIOnboardingMeta meta,
    String actionId,
) {
    switch (actionId) {
        case 'login':
            _login();
            break;
        case 'allow_notifications':
            _allowNotifications();
            break;
    }
}

// Embedded widget
onCustomAction: (meta, actionId) {
    _handleCustomAction(actionId);
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
  "actionId": "allowNotifications",
  "meta": {
    "onboardingId": "onboarding_123",
    "screenClientId": "profile_screen",
    "screenIndex": 0,
    "screensTotal": 3
  }
}
```
</Details>

### Hoàn thành tải onboarding \{#finishing-loading-onboarding\}

Khi onboarding hoàn tất việc tải, sự kiện này sẽ được kích hoạt:

```javascript showLineNumbers title="Flutter"
// Full-screen presentation
void onboardingViewDidFinishLoading(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
) {
  print('Onboarding loaded: ${meta.onboardingId}');
}

// Embedded widget
onDidFinishLoading: (meta) {
  print('Onboarding loaded: ${meta.onboardingId}');
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "welcome_screen",
        "screen_index": 0,
        "total_screens": 4
    }
}
```

</Details>

### Đóng onboarding \{#closing-onboarding\}

Onboarding được coi là đã đóng khi người dùng nhấn vào nút có hành động **Close** được gán.

  <img src="/assets/shared/img/ios-events-2.webp"
  style={{
    border: '1px solid #727272', /* border width and color */
    width: '700px', /* image width */
    display: 'block', /* for alignment */
    margin: '0 auto' /* center alignment */
  }}
/>

:::important
Lưu ý rằng bạn cần tự quản lý những gì xảy ra khi người dùng đóng onboarding. Ví dụ: bạn cần dừng hiển thị chính onboarding đó.
:::

```javascript showLineNumbers title="Flutter"
// Full-screen presentation
void onboardingViewOnCloseAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  await view.dismiss();
}

// Embedded widget
onCloseAction: (meta, actionId) {
  Navigator.of(context).pop();
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
  "action_id": "close_button",
  "meta": {
    "onboarding_id": "onboarding_123",
    "screen_cid": "final_screen",
    "screen_index": 3,
    "total_screens": 4
  }
}
```

</Details>

### Mở paywall \{#opening-a-paywall\}

:::tip
Xử lý sự kiện này để mở paywall nếu bạn muốn mở nó bên trong onboarding. Nếu bạn muốn mở paywall sau khi onboarding đóng, có một cách đơn giản hơn — xử lý sự kiện đóng và mở paywall mà không cần dựa vào dữ liệu sự kiện.
:::

Cách liền mạch nhất để làm việc với paywall trong onboarding là đặt action ID bằng với placement ID của paywall:

Lưu ý rằng, đối với iOS, chỉ một view (paywall hoặc onboarding) có thể được hiển thị trên màn hình cùng một lúc. Nếu bạn hiển thị paywall lên trên onboarding, bạn không thể điều khiển onboarding ở nền theo cách lập trình. Việc cố gắng dismiss onboarding sẽ đóng paywall thay vào đó, khiến onboarding vẫn còn hiển thị. Để tránh điều này, hãy luôn dismiss onboarding view trước khi hiển thị paywall.

```javascript showLineNumbers title="Flutter"
// Full-screen presentation
void onboardingViewOnPaywallAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // Dismiss onboarding before presenting paywall
  view.dismiss().then((_) {
    _openPaywall(actionId);
  });
}

Future<void> _openPaywall(String actionId) async {
  // Implement your paywall opening logic here
}

// Embedded widget
onPaywallAction: (meta, actionId) {
  _openPaywall(actionId);
}
```

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```json
{
    "action_id": "premium_offer_1",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "pricing_screen",
        "screen_index": 2,
        "total_screens": 4
    }
}
```

</Details>

### Theo dõi điều hướng \{#tracking-navigation\}

Bạn nhận được một analytics event khi có các sự kiện liên quan đến điều hướng xảy ra trong flow onboarding:

```javascript showLineNumbers title="Flutter"
// Full-screen presentation
void onboardingViewOnAnalyticsEvent(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  AdaptyOnboardingsAnalyticsEvent event,
) {
  trackEvent(event.type, meta.onboardingId);
}

// Embedded widget
onAnalyticsEvent: (meta, event) {
  trackEvent(event.type, meta.onboardingId);
}
```

Đối tượng `event` có thể là một trong các loại sau:

| Loại | Mô tả |
|------------|-------------|
| `onboardingStarted` | Khi onboarding đã được tải |
| `screenPresented` | Khi bất kỳ màn hình nào được hiển thị |
| `screenCompleted` | Khi một màn hình được hoàn thành. Bao gồm `elementId` tùy chọn (định danh của phần tử đã hoàn thành) và `reply` tùy chọn (phản hồi từ người dùng). Được kích hoạt khi người dùng thực hiện bất kỳ hành động nào để thoát khỏi màn hình. |
| `secondScreenPresented` | Khi màn hình thứ hai được hiển thị |
| `userEmailCollected` | Được kích hoạt khi email của người dùng được thu thập qua trường nhập liệu |
| `onboardingCompleted` | Được kích hoạt khi người dùng đến màn hình có ID `final`. Nếu bạn cần sự kiện này, hãy [gán ID `final` cho màn hình cuối](design-onboarding). |
| `unknown` | Dành cho bất kỳ loại sự kiện không được nhận diện nào. Bao gồm `name` (tên của sự kiện không xác định) và `meta` (metadata bổ sung) |

Mỗi sự kiện bao gồm thông tin `meta` chứa:

| Trường | Mô tả |
|------------|-------------|
| `onboardingId` | Định danh duy nhất của flow onboarding |
| `screenClientId` | Định danh của màn hình hiện tại |
| `screenIndex` | Vị trí của màn hình hiện tại trong flow |
| `screensTotal` | Tổng số màn hình trong flow |

<Details>
<summary>Ví dụ sự kiện (Nhấn để mở rộng)</summary>

```javascript
// onboardingStarted
{
  "name": "onboarding_started",
  "meta": {
    "onboarding_id": "onboarding_123",
    "screen_cid": "welcome_screen",
    "screen_index": 0,
    "total_screens": 4
  }
}

// screenPresented

{
    "name": "screen_presented",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "interests_screen",
        "screen_index": 2,
        "total_screens": 4
    }
}

// screenCompleted

{
    "name": "screen_completed",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    },
    "params": {
        "element_id": "profile_form",
        "reply": "success"
    }
}

// secondScreenPresented

{
    "name": "second_screen_presented",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    }
}

// userEmailCollected

{
    "name": "user_email_collected",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "profile_screen",
        "screen_index": 1,
        "total_screens": 4
    }
}

// onboardingCompleted

{
    "name": "onboarding_completed",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "final_screen",
        "screen_index": 3,
        "total_screens": 4
    }
}

```

</Details>