在 Flutter SDK 中处理用户引导事件

通过编辑工具配置的用户引导会生成各类事件,供应用响应处理。具体的处理方式取决于你所使用的展示方案:

  • 全屏展示:需要设置一个全局事件观察器,统一处理所有用户引导视图的事件
  • 嵌入式组件:直接通过组件内联回调参数处理事件

开始之前,请确保:

  1. 已安装 Adapty Flutter SDK 3.8.0 或更高版本。
  2. 创建用户引导
  3. 已将用户引导添加到版位

全屏展示事件

设置事件观察者

要处理全屏用户引导的事件,请实现 AdaptyUIOnboardingsEventsObserver 并在展示前进行设置:

AdaptyUI().setOnboardingsEventsObserver(this);

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

处理事件

在您的观察者中实现以下方法:

void onboardingViewDidFinishLoading(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
) {
  // 用户引导加载完成
}

void onboardingViewDidFailWithError(
  AdaptyUIOnboardingView view,
  AdaptyError error,
) {
  // 处理加载错误
}

void onboardingViewOnCloseAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // 处理关闭操作
  view.dismiss();
}

void onboardingViewOnPaywallAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // 展示付费墙前先关闭用户引导
  view.dismiss().then((_) {
    _openPaywall(actionId);
  });
}

void onboardingViewOnCustomAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  // 处理自定义操作
}

void onboardingViewOnStateUpdatedAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String elementId,
  AdaptyOnboardingsStateUpdatedParams params,
) {
  // 处理用户输入更新
}

void onboardingViewOnAnalyticsEvent(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  AdaptyOnboardingsAnalyticsEvent event,
) {
  // 追踪分析事件
}

嵌入式 Widget 事件

使用 AdaptyUIOnboardingPlatformView 时,你可以直接通过 Widget 内联回调参数处理事件。注意,事件会同时发送给 Widget 回调和全局观察者(如果已设置),但全局观察者是可选的:

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
  },
)

事件类型

以下章节介绍了您可以处理的各类事件,无论您使用哪种展示方式。

处理自定义操作

在编辑工具中,你可以为按钮添加 custom 操作并为其分配一个 ID。

ios-events-1.webp

之后,您可以在代码中使用该 ID,并将其作为自定义操作来处理。例如,当用户点击自定义按钮(如 LoginAllow notifications)时,委托方法 onboardingController 会以 .custom(id:) 的形式触发,actionId 参数即为编辑工具中设置的 Action ID。您可以自定义任意 ID,例如 “allowNotifications”。

// 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);
}
事件示例(点击展开)
{
  "actionId": "allowNotifications",
  "meta": {
    "onboardingId": "onboarding_123",
    "screenClientId": "profile_screen",
    "screenIndex": 0,
    "screensTotal": 3
  }
}

用户引导加载完成

当用户引导加载完成时,将触发以下事件:

// Full-screen presentation
void onboardingViewDidFinishLoading(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
) {
  print('Onboarding loaded: ${meta.onboardingId}');
}

// Embedded widget
onDidFinishLoading: (meta) {
  print('Onboarding loaded: ${meta.onboardingId}');
}
事件示例(点击展开)
{
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "welcome_screen",
        "screen_index": 0,
        "total_screens": 4
    }
}

关闭用户引导

当用户点击分配了 Close 操作的按钮时,用户引导即视为已关闭。

ios-events-2.webp

请注意,你需要自行处理用户关闭用户引导后的逻辑。例如,你需要停止显示用户引导界面本身。

// Full-screen presentation
void onboardingViewOnCloseAction(
  AdaptyUIOnboardingView view,
  AdaptyUIOnboardingMeta meta,
  String actionId,
) {
  await view.dismiss();
}

// Embedded widget
onCloseAction: (meta, actionId) {
  Navigator.of(context).pop();
}
事件示例(点击展开)
{
  "action_id": "close_button",
  "meta": {
    "onboarding_id": "onboarding_123",
    "screen_cid": "final_screen",
    "screen_index": 3,
    "total_screens": 4
  }
}

打开付费墙

如果希望在用户引导内部打开付费墙,请处理此事件。如果希望在付费墙关闭后再打开另一个付费墙,有一种更直接的方式——处理关闭操作并直接打开付费墙,无需依赖事件数据。

在用户引导中使用付费墙最顺畅的方式,是将操作 ID 设置为与付费墙版位 ID 相同:

请注意,在 iOS 上,同一时间只能显示一个视图(付费墙或用户引导)。如果在用户引导上方呈现付费墙,则无法以编程方式控制后台的用户引导。尝试关闭用户引导时,实际上会关闭付费墙,导致用户引导仍然可见。为避免此问题,请始终在呈现付费墙之前先关闭用户引导视图。

// 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);
}
事件示例(点击展开)
{
    "action_id": "premium_offer_1",
    "meta": {
        "onboarding_id": "onboarding_123",
        "screen_cid": "pricing_screen",
        "screen_index": 2,
        "total_screens": 4
    }
}

跟踪导航

在用户引导流程中,各种导航相关事件发生时,你都会收到对应的分析事件:

// 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);
}

event 对象可以是以下类型之一:

类型描述
onboardingStarted用户引导加载完成时触发
screenPresented任意屏幕显示时触发
screenCompleted屏幕完成时触发。包含可选的 elementId(已完成元素的标识符)和可选的 reply(用户的响应)。当用户执行任何退出屏幕的操作时触发。
secondScreenPresented第二个屏幕显示时触发
userEmailCollected通过输入框收集到用户邮箱时触发
onboardingCompleted当用户到达 ID 为 final 的屏幕时触发。如需使用此事件,请final ID 分配给最后一个屏幕
unknown用于任何无法识别的事件类型。包含 name(未知事件的名称)和 meta(附加元数据)
每个事件都包含 meta 元信息,具体字段如下:
字段描述
onboardingId用户引导流程的唯一标识符
screenClientId当前页面的标识符
screenIndex当前页面在流程中的位置
screensTotal流程中的总页面数
事件示例(点击展开)
// 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
    }
}