Handle onboarding events in Android SDK
Before you start, ensure that:
- You have installed Adapty Android SDK 3.8.0 or later.
- You have created an onboarding.
- You have added the onboarding to a placement.
Onboardings configured with the builder generate events your app can respond to. Learn how to respond to these events below.
To control or monitor processes occurring on the onboarding screen within your Android app, implement the AdaptyOnboardingEventListener
interface.
Custom actions
In the builder, you can add a custom action to a button and assign it an ID. Then, you can use this ID in your code and handle it as a custom action.

For example, if a user taps a custom button, like Login or Allow notifications, the delegate method onCustomAction
will be triggered with the action ID from the builder. You can create your own IDs, like "allowNotifications".
class YourActivity : AppCompatActivity() {
private val eventListener = object : AdaptyOnboardingEventListener {
override fun onCustomAction(action: AdaptyOnboardingCustomAction, context: Context) {
when (action.actionId) {
"allowNotifications" -> {
// Request notification permissions
}
}
}
override fun onError(error: AdaptyOnboardingError, context: Context) {
// Handle errors
}
// ... other required delegate methods
}
}
Event example (Click to expand)
{
"actionId": "allowNotifications",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
}
}
Closing onboarding
Onboarding is considered closed when a user taps a button with the Close action assigned. You need to manage what happens when a user closes the onboarding. For example:
You need to manage what happens when a user closes the onboarding. For instance, you need to stop displaying the onboarding itself.
For example:
override fun onCloseAction(action: AdaptyOnboardingCloseAction, context: Context) {
// Dismiss the onboarding screen
(context as? Activity)?.onBackPressed()
}
Event example (Click to expand)
{
"action_id": "close_button",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "final_screen",
"screen_index": 3,
"total_screens": 4
}
}
Updating field state
When your users respond to a quiz question or input their data into an input field, the onStateUpdatedAction
method will be invoked. You can save or process the field type in your code.
For example:
override fun onStateUpdatedAction(action: AdaptyOnboardingStateUpdatedAction, context: Context) {
// Store user preferences or responses
when (val params = action.params) {
is AdaptyOnboardingStateUpdatedParams.Select -> {
// Handle single selection
saveUserPreference(elementId = action.elementId, value = params.value)
}
is AdaptyOnboardingStateUpdatedParams.MultiSelect -> {
// Handle multiple selections
saveUserPreferences(elementId = action.elementId, values = params.map { it.value })
}
is AdaptyOnboardingStateUpdatedParams.Input -> {
// Handle text input
saveUserInput(elementId = action.elementId, value = params.value)
}
is AdaptyOnboardingStateUpdatedParams.DatePicker -> {
// Handle date selection
saveUserDate(elementId = action.elementId, value = "${params.month}-${params.day}-${params.year}")
}
}
}
This example suggests you implement custom methods for saving user data depending on the data type. These methods are not built into the Adapty SDK.
The action
object contains:
elementId
: A unique identifier for the input element. You can use it to associate questions with answers when saving them.params
: The user's input data, which can be one of the following types:Select
: Single selection from a list of options.MultiSelect
: Multiple selections from a list of options.Input
: Text input from the user.DatePicker
: Date selected by the user.
Saved data examples (Click to expand)
// Example of a saved select action
{
"elementId": "preference_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "preferences_screen",
"screenIndex": 1,
"screensTotal": 3
},
"params": {
"type": "select",
"value": {
"id": "option_1",
"value": "premium",
"label": "Premium Plan"
}
}
}
// Example of a saved multi-select action
{
"elementId": "interests_selector",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "interests_screen",
"screenIndex": 2,
"screensTotal": 3
},
"params": {
"type": "multiSelect",
"value": [
{
"id": "interest_1",
"value": "sports",
"label": "Sports"
},
{
"id": "interest_2",
"value": "music",
"label": "Music"
}
]
}
}
// Example of a saved input action
{
"elementId": "name_input",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "input",
"value": {
"type": "text",
"value": "John Doe"
}
}
}
// Example of a saved date picker action
{
"elementId": "birthday_picker",
"meta": {
"onboardingId": "onboarding_123",
"screenClientId": "profile_screen",
"screenIndex": 0,
"screensTotal": 3
},
"params": {
"type": "datePicker",
"value": {
"day": 15,
"month": 6,
"year": 1990
}
}
}
Opening a paywall
Handle this event to open a paywall if you want to open it inside the onboarding. If you want to open a paywall after it is closed, there is a more straightforward way to do it – handle AdaptyOnboardingCloseAction
and open a paywall without relying on the event data.
If a user clicks a button that opens a paywall, you will get a button action ID that you set up manually. The most seamless way to work with paywalls in onboardings is to make the action ID equal to a paywall placement ID. This way, after the AdaptyOnboardingOpenPaywallAction
, you can use the placement ID to get and open the paywall right away:
override fun onOpenPaywallAction(action: AdaptyOnboardingOpenPaywallAction, context: Context) {
// Get the paywall using the placement ID from the action
Adapty.getPaywall(placementId = action.actionId) { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
// Get the paywall configuration
AdaptyUI.getViewConfiguration(paywall) { result ->
when(result) {
is AdaptyResult.Success -> {
val paywallConfig = result.value
// Create and present the paywall
val paywallView = AdaptyUI.getPaywallView(
activity = this,
viewConfig = paywallConfig,
products,
eventListener = paywallEventListener
)
// Add the paywall view to your layout
binding.container.addView(paywallView)
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// handle the error
}
}
}
}
Event example (Click to expand)
{
"action_id": "premium_offer_1",
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "pricing_screen",
"screen_index": 2,
"total_screens": 4
}
}
Finishing loading onboarding
When an onboarding finishes loading, this method will be invoked:
override fun onFinishLoading(action: AdaptyOnboardingLoadedAction, context: Context) {
// Handle loading completion
}
Event example (Click to expand)
{
"meta": {
"onboarding_id": "onboarding_123",
"screen_cid": "welcome_screen",
"screen_index": 0,
"total_screens": 4
}
}
Navigation events
The onAnalyticsEvent
method is called when various analytics events occur during the onboarding flow.
The event
object can be one of the following types:
Type | Description |
---|---|
OnboardingStarted | When the onboarding has been loaded |
ScreenPresented | When any screen is shown |
ScreenCompleted | When a screen is completed. Includes optional elementId (identifier of the completed element) and optional reply (response from the user). Triggered when users perform any action to exit the screen. |
SecondScreenPresented | When the second screen is shown |
UserEmailCollected | Triggered when the user's email is collected via the input field |
OnboardingCompleted | Triggered when a user reaches a screen with the final ID. If you need this event, assign the final ID to the last screen. |
Unknown | For any unrecognized event type. Includes name (the name of the unknown event) and meta (additional metadata) |
Each event includes meta
information containing:
Field | Description |
---|---|
onboardingId | Unique identifier of the onboarding flow |
screenClientId | Identifier of the current screen |
screenIndex | Current screen's position in the flow |
totalScreens | Total number of screens in the flow |
Here's an example of how you can use analytics events for tracking:
override fun onAnalyticsEvent(event: AdaptyOnboardingAnalyticsEvent, context: Context) {
when (event) {
is AdaptyOnboardingAnalyticsEvent.OnboardingStarted -> {
// Track onboarding start
trackEvent("onboarding_started", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.ScreenPresented -> {
// Track screen presentation
trackEvent("screen_presented", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.ScreenCompleted -> {
// Track screen completion with user response
trackEvent("screen_completed", event.meta, event.elementId, event.reply)
}
is AdaptyOnboardingAnalyticsEvent.OnboardingCompleted -> {
// Track successful onboarding completion
trackEvent("onboarding_completed", event.meta)
}
is AdaptyOnboardingAnalyticsEvent.Unknown -> {
// Handle unknown events
trackEvent(event.name, event.meta)
}
// Handle other cases as needed
}
}
Event examples (Click to expand)
// 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
}
}