Flutter - Handle paywall events
Paywalls configured with the Paywall Builder don't need extra code to make and restore purchases. However, they generate some events that your app can respond to. Those events include button presses (close buttons, URLs, product selections, and so on) as well as notifications on purchase-related actions taken on the paywall. Learn how to respond to these events below.
This guide is for new Paywall Builder paywalls only which require Adapty SDK v3.0 or later. For presenting paywalls in Adapty SDK v2 designed with legacy Paywall Builder, see Flutter - Handle paywall events designed with legacy Paywall Builder.
To control or monitor processes occurring on the paywall screen within your mobile app, implement the AdaptyUIPaywallsEventsObserver
methods and set the observer before presenting any screen:
AdaptyUI().setPaywallsEventsObserver(this);
Want to see a real-world example of how Adapty SDK is integrated into a mobile app? Check out our sample apps, which demonstrate the full setup, including displaying paywalls, making purchases, and other basic functionality.
User-generated events
Actions
If a user has performed some action, this method will be invoked:
// You have to install url_launcher plugin in order to handle urls:
// https://pub.dev/packages/url_launcher
import 'package:url_launcher/url_launcher_string.dart';
void paywallViewDidPerformAction(AdaptyUIPaywallView view, AdaptyUIAction action) {
switch (action) {
case const CloseAction():
case const AndroidSystemBackAction():
view.dismiss();
break;
case OpenUrlAction(url: final url):
final Uri uri = Uri.parse(url);
launchUrl(uri, mode: LaunchMode.inAppBrowserView);
break;
default:
break;
}
}
Event examples (Click to expand)
// Close action
{
"action": "CloseAction"
}
// Android system back action
{
"action": "AndroidSystemBackAction"
}
// Open URL action
{
"action": "OpenUrlAction",
"url": "https://example.com/terms"
}
// Custom action
{
"action": "CustomAction",
"value": "login"
}
The following action types are supported:
CloseAction
AndroidSystemBackAction
OpenUrlAction
CustomAction
Note that at the very least you need to implement the reactions to both close
and openURL
.
For example, if a user taps the close button, the action close
will occur and you are supposed to dismiss the paywall. Refer to the Hide Paywall Builder paywalls topic for details on dismissing a paywall screen.
Note that AdaptyUIAction
has optional value property: look at this in the case of openUrl
and custom
.
💡 Login Action
If you have configured Login Action in the dashboard, you should implement reaction for
custom
action with value"login"
Product selection
If a product is selected for purchase (by a user or by the system), this method will be invoked:
void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) {
}
Event example (Click to expand)
{
"productId": "premium_monthly"
}
Started purchase
If a user initiates the purchase process, this method will be invoked:
void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) {
}
Event example (Click to expand)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
Successful purchase
If Adapty.makePurchase()
succeeds, this method will be invoked:
void paywallViewDidFinishPurchase(AdaptyUIPaywallView view,
AdaptyPaywallProduct product,
AdaptyPurchaseResult purchaseResult) {
switch (purchaseResult) {
case AdaptyPurchaseResultSuccess(profile: final profile):
// successful purchase
break;
case AdaptyPurchaseResultPending():
// purchase is pending
break;
case AdaptyPurchaseResultUserCancelled():
// user cancelled the purchase
break;
default:
break;
}
}
Event examples (Click to expand)
// Successful purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "AdaptyPurchaseResultSuccess",
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
}
}
}
}
// Pending purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "AdaptyPurchaseResultPending"
}
}
// User cancelled purchase
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"purchaseResult": {
"type": "AdaptyPurchaseResultUserCancelled"
}
}
We recommend dismissing the screen in that case. Refer to the Hide Paywall Builder paywalls for details on dismissing a paywall screen.
Failed purchase
If Adapty.makePurchase()
fails, this method will be invoked:
void paywallViewDidFailPurchase(AdaptyUIPaywallView view,
AdaptyPaywallProduct product,
AdaptyError error) {
}
Event example (Click to expand)
{
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
},
"error": {
"code": "purchase_failed",
"message": "Purchase failed due to insufficient funds",
"details": {
"underlyingError": "Insufficient funds in account"
}
}
}
Successful restore
If Adapty.restorePurchases()
succeeds, this method will be invoked:
void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
}
Event example (Click to expand)
{
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
},
"subscriptions": [
{
"vendorProductId": "premium_monthly",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
]
}
}
We recommend dismissing the screen if the user has the required accessLevel
. Refer to the Subscription status topic to learn how to check it and to Hide Paywall Builder paywalls topic to learn how to dismiss a paywall screen.
Failed restore
If Adapty.restorePurchases()
fails, this method will be invoked:
void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
}
Event example (Click to expand)
{
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}
Data fetching and rendering
Product loading errors
If you don't pass the product array during the initialization, AdaptyUI will retrieve the necessary objects from the server by itself. If this operation fails, AdaptyUI will report the error by invoking this method:
void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
}
Event example (Click to expand)
{
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}
Rendering errors
If an error occurs during the interface rendering, it will be reported by calling this method:
void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) {
}
Event example (Click to expand)
{
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}
In a normal situation, such errors should not occur, so if you come across one, please let us know.