PaywallPresentationHandler

A handler class that provides status updates for paywall presentation in registerPlacement() calls.

Use this handler when you need fine-grained control over paywall events for a specific registerPlacement() call, rather than global events via SuperwallDelegate.

This handler is specific to the individual registerPlacement() call. For global paywall events across your app, use SuperwallDelegate instead.

Purpose

Provides callbacks for paywall lifecycle events when using registerPlacement() with a specific handler instance.

Signature

class PaywallPresentationHandler {
  void onPresent(Function(PaywallInfo) handler);
  void onDismiss(Function(PaywallInfo, PaywallResult) handler);
  void onSkip(Function(PaywallSkippedReason) handler);
  void onError(Function(String) handler);
  void onCustomCallback(
    Future<CustomCallbackResult> Function(CustomCallback) handler,
  );
}

Parameters

Prop

Type

Returns / State

Each method returns void and configures the handler for the specific paywall lifecycle event.

CustomCallback (2.4.8+)

The onCustomCallback handler receives a CustomCallback object.

Prop

Type

CustomCallbackResult (2.4.8+)

Return a CustomCallbackResult from your callback handler to control paywall flow:

CustomCallbackResult.success([Map<String, Object>? data])
CustomCallbackResult.failure([Map<String, Object>? data])

Use data to send values back to the paywall, available as callbacks.<name>.data.<key>.

PaywallInfo State (2.4.8+)

PaywallInfo now includes state, a Map<String, Object>? with current paywall state values.

Usage

Basic handler setup:

Future<void> _registerFeatureWithHandler() async {
  final handler = PaywallPresentationHandler();

  handler.onPresent((paywallInfo) {
    print('Paywall presented: ${paywallInfo.identifier}');
    print('Paywall state: ${paywallInfo.state}');
  });

  handler.onDismiss((paywallInfo, result) {
    print('Paywall dismissed with result: $result');

    switch (result) {
      case PaywallResult.purchased:
        _showSuccessMessage();
        break;
      case PaywallResult.cancelled:
        _showPromotionalOffer();
        break;
      case PaywallResult.restored:
        _updateUIForActiveSubscription();
        break;
    }
  });

  await Superwall.shared.registerPlacement(
    'premium_feature',
    params: {'source': 'feature_screen'},
    handler: handler,
    feature: () {
      _unlockPremiumFeature();
    },
  );
}

Handle skip, error, and custom callbacks:

Future<void> _setupComprehensiveHandler() async {
  final handler = PaywallPresentationHandler();

  handler.onSkip((reason) {
    print('Paywall skipped: $reason');

    switch (reason) {
      case PaywallSkippedReason.userIsSubscribed:
        _proceedToFeature();
        break;
      case PaywallSkippedReason.holdout:
        _proceedToFeature();
        break;
      default:
        break;
    }
  });

  handler.onError((error) {
    print('Paywall error: $error');
    _showErrorDialog(error);
  });

  handler.onCustomCallback((callback) async {
    switch (callback.name) {
      case 'validate_email':
        final email = callback.variables?['email'] as String?;
        if (email != null && email.contains('@')) {
          return CustomCallbackResult.success({'validated': true});
        }
        return CustomCallbackResult.failure({'error': 'Invalid email'});
      default:
        return CustomCallbackResult.failure({'error': 'Unknown callback'});
    }
  });

  await Superwall.shared.registerPlacement(
    'remove_ads',
    handler: handler,
    feature: () {
      _hideAdsFromUI();
    },
  );
}

How is this guide?

On this page