Skip to content

Add full screen paywall presentation support#839

Merged
vegaro merged 4 commits into
mainfrom
cesar/pw-956-implement-per-platform-paywall-presentation-style-api-in
Mar 11, 2026
Merged

Add full screen paywall presentation support#839
vegaro merged 4 commits into
mainfrom
cesar/pw-956-implement-per-platform-paywall-presentation-style-api-in

Conversation

@vegaro

@vegaro vegaro commented Feb 27, 2026

Copy link
Copy Markdown
Member

Adds a new PaywallPresentationConfiguration API that allows developers to control how paywalls are presented on each platform independently. Currently, iOS presents paywalls as a modal sheet while Android uses full screen. This PR enables iOS fullscreen presentation with an API that is extensible for future platform-specific modes without breaking changes.

Developers have reported wanting full-screen paywall presentation on iOS to match Android behavior (CC-596 (​https://linear.app/revenuecat/issue/CC-596​), PW-891 (​https://linear.app/revenuecat/issue/PW-891​)). The underlying native support already exists in purchases-hybrid-common via PaywallProxy's useFullScreenPresentation option.

API Design

Uses sealed classes with private constructors instead of enums, so new presentation styles can be added in the future without breaking existing code.

  // Present paywall in full screen on iOS (Android is always full screen)
  var options = new PaywallOptions(
      presentationConfiguration: new PaywallPresentationConfiguration(
          ios: IOSPaywallPresentationStyle.FullScreen
      )
  );
  await PaywallsPresenter.Present(options);
  // Existing code continues to work unchanged (sheet on iOS, full screen on Android)
  await PaywallsPresenter.Present();

These types make unsupported combinations a compile error:

  • IOSPaywallPresentationStyle: FullScreen, Sheet
  • AndroidPaywallPresentationStyle: FullScreen (only option for now, since Android doesn't support sheet yet and we don't want to add it yet)

Just to be clear, we don't want to add support for modal in Android yet, we haven't got any request for that and it's a bit complex, so we are looking for adding full screen support to iOS.

/// <summary>
/// Controls how paywalls are presented on iOS.
/// </summary>
public sealed class IOSPaywallPresentationStyle

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I guess I'm not sure if we should make these sealed... If it allows to do some sort of switch-case style processing on the styles, I would say it could produce a breaking change, but seems like it shouldn't, so I think we should be ok.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think enums in C# are not exhaustive. That would work as well right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, as long as they do not allow for exhaustive handling of all values, we should be ok I think.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked into this and the sealed class approach is safe here. In C#, sealed class with private constructor + static readonly fields is not exhaustively matched by the compiler, unlike enum or Kotlin sealed class. If someone writes:

  if (style == IOSPaywallPresentationStyle.FullScreen) { ... }
  else if (style == IOSPaywallPresentationStyle.Sheet) { ... }                                                             

Adding a new style later won't cause a compile error, they just won't handle it (safe default). With a C# enum, the compiler can warn about unhandled cases in switch, making new values potentially breaking.

vegaro added 2 commits March 11, 2026 13:47
Adds `FullScreen` and `Default` static presets for common presentation
configurations, simplifying the most common use case. Also updates the
Subtester to use the new preset.

Made-with: Cursor
@vegaro vegaro force-pushed the cesar/pw-956-implement-per-platform-paywall-presentation-style-api-in branch from 46b7e3c to 8342045 Compare March 11, 2026 12:48

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thank you!!

static NSMutableDictionary *RCUICreateOptionsDictionary(NSString *offeringIdentifier, NSString *presentedOfferingContextJson, BOOL displayCloseButton, BOOL useFullScreenPresentation) {
NSMutableDictionary *options = [NSMutableDictionary new];
options[kRCUIOptionDisplayCloseButton] = @(displayCloseButton);
options[@"useFullScreenPresentation"] = @(useFullScreenPresentation);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should pass the presentation mode as a string instead... But I guess since this is internal, we can always change it later. Let's keep it like this!

/// Configuration for how a paywall should be presented on each platform.
/// Each platform field is optional; when null, the platform's default presentation style is used.
/// </summary>
public class PaywallPresentationConfiguration

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would be worth adding a small code sample on how to use this in the docs either here or the PaywallOptions... I've found that helps figuring out how to use these APIs... But not a blocker, I think it can always be added later if needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vegaro vegaro enabled auto-merge (squash) March 11, 2026 13:30
@vegaro vegaro disabled auto-merge March 11, 2026 14:20
@vegaro vegaro merged commit 13c8bfd into main Mar 11, 2026
8 checks passed
@vegaro vegaro deleted the cesar/pw-956-implement-per-platform-paywall-presentation-style-api-in branch March 11, 2026 14:20
ajpallares pushed a commit that referenced this pull request Mar 11, 2026
**This is an automatic release.**

## RevenueCat SDK
### ✨ New Features
* Add PurchaseLogic support for paywall presentation when
`PurchasesAreCompletedBy.MyApp` (#842) via Toni Rico (@tonidero)
### 📦 Dependency Updates
* [AUTOMATIC BUMP] Updates purchases-hybrid-common to 17.46.1 (#854) via
RevenueCat Git Bot (@RCGitBot)
* [Android
9.23.1](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.1)
* [Android
9.23.0](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.0)
* [iOS
5.61.0](https://github.com/RevenueCat/purchases-ios/releases/tag/5.61.0)
* [AUTOMATIC BUMP] Updates purchases-hybrid-common to 17.46.0 (#853) via
RevenueCat Git Bot (@RCGitBot)
* [Android
9.23.1](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.1)
* [Android
9.23.0](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.0)
* [iOS
5.61.0](https://github.com/RevenueCat/purchases-ios/releases/tag/5.61.0)
* [AUTOMATIC BUMP] Updates purchases-hybrid-common to 17.44.0 (#850) via
RevenueCat Git Bot (@RCGitBot)
* [Android
9.23.1](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.1)
* [Android
9.23.0](https://github.com/RevenueCat/purchases-android/releases/tag/9.23.0)
* [iOS
5.61.0](https://github.com/RevenueCat/purchases-ios/releases/tag/5.61.0)

## RevenueCatUI SDK
### ✨ New Features
* Add full screen paywall presentation support (#839) via Cesar de la
Vega (@vegaro)

### 🔄 Other Changes
* Redo Subtester app and fix Android emulators support (#855) via Cesar
de la Vega (@vegaro)
* Support PaywallActivity screenOrientation manifest override (#844) via
Toni Rico (@tonidero)
* refactor: Use PaywallView instead of PaywallActivity on Android (#841)
via Toni Rico (@tonidero)
* Add API tests for Paywalls and CustomerCenter presenters (#735) via
Facundo Menzella (@facumenzella)
* Bump fastlane-plugin-revenuecat_internal from `f5c099b` to `e146447`
(#852) via dependabot[bot] (@dependabot[bot])
* Bump fastlane-plugin-revenuecat_internal from `8cd957f` to `f5c099b`
(#848) via dependabot[bot] (@dependabot[bot])
facumenzella added a commit to RevenueCat/purchases-flutter that referenced this pull request Mar 20, 2026
…tation style (#1623)

## Summary

Fixes landscape white-area issue (PW-148) by adding an explicit opt-in
API for controlling paywall presentation style, matching the approach in
[purchases-unity PR
#839](RevenueCat/purchases-unity#839).

- Adds \`PaywallPresentationConfiguration\` with per-platform style
types (\`IOSPaywallPresentationStyle\`,
\`AndroidPaywallPresentationStyle\`)
- Adds optional \`presentationConfiguration\` parameter to
\`presentPaywall()\` and \`presentPaywallIfNeeded()\`
- iOS plugin passes \`useFullScreenPresentation\` only when explicitly
set — default behavior unchanged (sheet on iOS)
- No device filtering — if the dev passes \`fullScreen\`, it applies on
any device (iPhone or iPad)
- Purchase tester shows a \`CupertinoActionSheet\` to choose Sheet or
Full Screen before presenting

### Usage

```dart
// Full screen on iOS (fixes landscape white-area bug PW-148)
final result = await RevenueCatUI.presentPaywall(
  presentationConfiguration: PaywallPresentationConfiguration(
    ios: IOSPaywallPresentationStyle.fullScreen,
  ),
);

// Default behavior (sheet on iOS)
final result = await RevenueCatUI.presentPaywall();
```

### Related

- Unity: RevenueCat/purchases-unity#839
- Linear: PW-148, PW-956

## Test plan

- [x] All unit tests pass
- [x] API tester (\`flutter analyze\`) clean — no breaking changes
- [x] Tests cover \`fullScreen\` and \`sheet\` configurations
- [x] Tests verify \`useFullScreenPresentation\` is not sent when no
config is passed
- [x] Purchase tester builds on iOS
- [x] Manual test: present paywall without config → sheet on iOS
- [x] Manual test: present paywall with \`ios: fullScreen\` → full
screen on iOS

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a new public API knob that changes iOS paywall presentation
behavior and updates the iOS plugin call path; incorrect wiring could
affect paywall UX on iOS. Changes are scoped and covered by new unit/API
tests, reducing regression risk.
> 
> **Overview**
> Adds an opt-in `PaywallPresentationConfiguration` API (with
`IOSPaywallPresentationStyle`/`AndroidPaywallPresentationStyle`) and
exports it from `purchases_ui_flutter`.
> 
> `RevenueCatUI.presentPaywall()` and `presentPaywallIfNeeded()` now
accept an optional `presentationConfiguration` and only send the native
`useFullScreenPresentation` flag when iOS full-screen is explicitly
requested (omitting the key preserves the native default sheet
behavior).
> 
> Updates the iOS plugin to accept/pass through
`useFullScreenPresentation` instead of always forcing full-screen on
iPhone, expands unit tests/API tester coverage for the new argument
behavior, and updates the purchase tester example UI to let users choose
sheet vs full-screen before presenting a paywall.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
35fb97d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cesar de la Vega <664544+vegaro@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants