Migrate Adapty Android SDK to v. 4.0
Adapty Android SDK 4.0 (beta) introduces flows and renames the paywall APIs accordingly. The new APIs work with both the new Flow Builder and the existing Paywall Builder — no setup changes are required on the Adapty Dashboard side.
Quick reference
| v3 | v4 |
|---|---|
Adapty.getPaywall(placementId, locale) | Adapty.getFlow(placementId) |
Adapty.getPaywallForDefaultAudience(placementId, locale) | Adapty.getFlowForDefaultAudience(placementId) |
AdaptyUI.getViewConfiguration(paywall) | AdaptyUI.getFlowConfiguration(flow, locale) |
AdaptyUI.LocalizedViewConfiguration | AdaptyUI.FlowConfiguration |
Adapty.getPaywallProducts(paywall) | Adapty.getPaywallProducts(flow) |
Adapty.logShowPaywall(paywall) | Adapty.logShowFlow(flow) |
AdaptyPaywall | AdaptyFlow |
AdaptyUI.getPaywallView(...) | AdaptyUI.getFlowView(...) |
AdaptyPaywallView | AdaptyFlowView |
AdaptyPaywallScreen (Compose) | AdaptyFlowScreen |
showPaywall(...) | showFlow(...) |
AdaptyPaywallInsets | AdaptyFlowInsets |
AdaptyUiEventListener | AdaptyFlowEventListener |
AdaptyUiDefaultEventListener | AdaptyFlowDefaultEventListener |
onPaywallShown / onPaywallClosed | onFlowShown / onFlowClosed |
onRenderingError | onError |
Adapty.updateAttribution(attribution, source) (source: String) | Adapty.updateAttribution(attribution, source) (source: AdaptyAttributionSource) |
Adapty.setIntegrationIdentifier(key, value) | Adapty.setIntegrationIdentifier(AdaptyIntegrationIdentifier) |
AdaptyPaywallProduct keeps its name — products still belong to a flow, and getPaywallProducts now takes an AdaptyFlow. The other AdaptyFlowEventListener methods (onProductSelected, onPurchaseStarted, onPurchaseFinished, onPurchaseFailure, onRestoreSuccess, onRestoreFailure, onActionPerformed, onAwaitingPurchaseParams, onLoadingProductsFailure, and so on) keep their names and signatures.
Installation
Set the adapty-bom version to 4.0.0-beta.1 and sync the project. The BOM resolves the matching android-sdk and android-ui versions for you. See Install Adapty SDK for the dependency declarations.
Removed and deprecated APIs
Adapty.makePurchase(activity, product, subscriptionUpdateParams, isOfferPersonalized, callback)— removed. This overload was deprecated in v3. Pass the same options throughAdaptyPurchaseParametersinstead:
- Adapty.makePurchase(activity, product, subscriptionUpdateParams, isOfferPersonalized) { result -> /* ... */ }
+ val params = AdaptyPurchaseParameters.Builder()
+ .withSubscriptionUpdateParams(subscriptionUpdateParams)
+ .withOfferPersonalized(isOfferPersonalized)
+ .build()
+ Adapty.makePurchase(activity, product, params) { result -> /* ... */ }
- Onboardings are deprecated.
AdaptyUI.getOnboardingViewandAdaptyUI.getOnboardingConfigurationare marked@Deprecatedin 4.0 — migrate onboardings to flows built in the Flow Builder.
Fetching flows
getPaywall + getViewConfiguration → getFlow + getFlowConfiguration
The fetch return type changes from AdaptyPaywall to AdaptyFlow, and the configuration loader is renamed from AdaptyUI.getViewConfiguration to AdaptyUI.getFlowConfiguration (returning AdaptyUI.FlowConfiguration instead of AdaptyUI.LocalizedViewConfiguration). The locale parameter moves out of the fetch call and onto getFlowConfiguration:
- Adapty.getPaywall("YOUR_PLACEMENT_ID", locale = "en") { result ->
+ Adapty.getFlow("YOUR_PLACEMENT_ID") { result ->
if (result is AdaptyResult.Success) {
- val paywall = result.value
- if (!paywall.hasViewConfiguration) return@getPaywall
- AdaptyUI.getViewConfiguration(paywall) { configResult ->
+ val flow = result.value
+ if (!flow.hasViewConfiguration) return@getFlow
+ AdaptyUI.getFlowConfiguration(flow, locale = "en") { configResult ->
if (configResult is AdaptyResult.Success) {
val flowConfiguration = configResult.value
}
}
}
}
getPaywallProducts(paywall) → getPaywallProducts(flow)
getPaywallProducts now takes an AdaptyFlow returned by Adapty.getFlow:
- Adapty.getPaywallProducts(paywall) { result -> /* products */ }
+ Adapty.getPaywallProducts(flow) { result -> /* products */ }
Tracking flow views
logShowPaywall → logShowFlow
logShowPaywall is renamed to logShowFlow and now takes an AdaptyFlow instead of an AdaptyPaywall. The event is still logged against the same variation, so existing funnel and A/B test metrics continue to work without dashboard changes.
- Adapty.logShowPaywall(paywall)
+ Adapty.logShowFlow(flow)
As in v3, you do not need to call this method when displaying flows or paywalls rendered by the Flow Builder or the Paywall Builder — Adapty tracks those views automatically.
Displaying flows
getPaywallView / AdaptyPaywallView → getFlowView / AdaptyFlowView
Rename the factory method and the view type, and pass the AdaptyUI.FlowConfiguration:
- val paywallView = AdaptyUI.getPaywallView(
- activity,
- viewConfiguration,
- products,
- eventListener,
- )
+ val flowView = AdaptyUI.getFlowView(
+ activity,
+ flowConfiguration,
+ products,
+ eventListener,
+ )
If you create the view directly, the show method is renamed too:
- val paywallView = AdaptyPaywallView(activity)
- paywallView.showPaywall(viewConfiguration, products, eventListener)
+ val flowView = AdaptyFlowView(activity)
+ flowView.showFlow(flowConfiguration, products, eventListener)
In XML layouts, update the view tag:
- <com.adapty.ui.AdaptyPaywallView ... />
+ <com.adapty.ui.AdaptyFlowView ... />
The optional personalizedOfferResolver parameter has been removed from getFlowView / showFlow / AdaptyFlowScreen. To indicate personalized pricing, set it per product through onAwaitingPurchaseParams (AdaptyPurchaseParameters.Builder().withOfferPersonalized(true)). A new optional customAssets parameter lets you override images and videos at runtime — see Customize assets.
AdaptyPaywallScreen → AdaptyFlowScreen
In Jetpack Compose, rename the composable and update the configuration parameter:
- AdaptyPaywallScreen(
- viewConfiguration,
+ AdaptyFlowScreen(
+ flowConfiguration,
products,
eventListener,
)
Handling events
The event listener is renamed from AdaptyUiEventListener to AdaptyFlowEventListener (and AdaptyUiDefaultEventListener to AdaptyFlowDefaultEventListener). Most method names are unchanged; the lifecycle and rendering callbacks are renamed:
- class YourListener : AdaptyUiDefaultEventListener() {
+ class YourListener : AdaptyFlowDefaultEventListener() {
- override fun onPaywallShown(context: Context) {}
- override fun onPaywallClosed() {}
+ override fun onFlowShown(context: Context) {}
+ override fun onFlowClosed() {}
- override fun onRenderingError(error: AdaptyError, context: Context) {}
+ override fun onError(error: AdaptyError, context: Context) {}
}
Existing handler bodies don’t need code changes — just rename the type and the overrides. onError fires for the same rendering errors as onRenderingError did, plus other non-purchase runtime errors. See Handle flow & paywall events for the full list of callbacks.
v4 also adds an onBackPressed(context): Boolean callback, and its default changes how the system back button behaves. Previously the back button (or back gesture) fell through to your activity or fragment, which typically dismissed the paywall. In v4 the default implementation consumes the press, so the system back button no longer closes a flow on its own — matching iOS, where a flow can’t be dismissed by a system gesture. Give users an explicit way out (a Close button or an on_device_back action), or override onBackPressed to return false to restore the old behavior. See System back button for details.
The default purchase handler also stops dismissing the screen. In v3, the default onPurchaseFinished closed the paywall after any finished purchase that wasn’t a user cancellation (a successful or pending purchase). In v4 it’s a no-op, so a flow stays open after a purchase until you dismiss it — again matching iOS. If you relied on that auto-close, dismiss the screen yourself once the purchase finishes. See Successful, canceled, or pending purchase for an example.
Attribution and integration identifiers
updateAttribution
The source parameter changes from String to the new AdaptyAttributionSource type, and attribution is now a Map<String, Any> (a JSON String overload is also available). Use one of the predefined sources:
- Adapty.updateAttribution(attribution, "appsflyer") { error -> /* handle the error */ }
+ Adapty.updateAttribution(attribution, AdaptyAttributionSource.APPSFLYER) { error -> /* handle the error */ }
Predefined sources: AdaptyAttributionSource.APPLE_ADS, .ADJUST, .APPSFLYER, .BRANCH, .TENJIN. For any other source, construct one from a string: AdaptyAttributionSource("your_source").
setIntegrationIdentifier
setIntegrationIdentifier(key, value) is replaced by a method that takes one or more AdaptyIntegrationIdentifier values. Build each identifier with a convenience method instead of passing a raw string key:
- Adapty.setIntegrationIdentifier("appsflyer_id", appsFlyerId) { error -> /* handle the error */ }
+ Adapty.setIntegrationIdentifier(AdaptyIntegrationIdentifier.appsflyerId(appsFlyerId)) { error -> /* handle the error */ }
You can set several identifiers in a single call:
Adapty.setIntegrationIdentifier(
listOf(
AdaptyIntegrationIdentifier.appsflyerId(appsFlyerId),
AdaptyIntegrationIdentifier.adjustDeviceId(adjustDeviceId),
)
) { error -> /* handle the error */ }
Replace each old key string with its convenience method:
| v3 key | v4 AdaptyIntegrationIdentifier method |
|---|---|
"adjust_device_id" | adjustDeviceId(value) |
"airbridge_device_id" | airbridgeDeviceId(value) |
"amplitude_user_id" | amplitudeUserId(value) |
"amplitude_device_id" | amplitudeDeviceId(value) |
"appmetrica_device_id" | appmetricaDeviceId(value) |
"appmetrica_profile_id" | appmetricaProfileId(value) |
"appsflyer_id" | appsflyerId(value) |
"branch_id" | branchId(value) |
"facebook_anonymous_id" | facebookAnonymousId(value) |
"firebase_app_instance_id" | firebaseAppInstanceId(value) |
"mixpanel_user_id" | mixpanelUserId(value) |
"one_signal_subscription_id" | oneSignalSubscriptionId(value) |
"one_signal_player_id" | oneSignalPlayerId(value) |
"posthog_distinct_user_id" | posthogDistinctUserId(value) |
"pushwoosh_hwid" | pushwooshHWID(value) |
"tenjin_analytics_installation_id" | tenjinAnalyticsInstallationId(value) |
For a key that isn’t in this list, build the identifier directly from a custom Key: AdaptyIntegrationIdentifier(AdaptyIntegrationIdentifier.Key("custom"), customValue).