type: "paywall"), but the paywall UI itself is rendered by your app — Flowboard never embeds a purchase SDK.
Unlike a custom screen, a paywall screen is routed by type, not by id: you register one paywallScreenBuilder and it serves every paywall screen in every flow. Each screen keeps its own unique id, so a flow can contain several paywalls (for example one early and one at the end) and Pulse shows them as separate funnel steps.
The builder receives the same context object as customScreenBuilder:
ctx.screenData: the JSON for the current paywall screen — read the placement from itsproperties.placementctx.formData: the data already collected in the flowctx.onNext(): move to the next screenctx.onPrevious(): move to the previous screenctx.onFinish(): end the flowctx.onJumpTo(screenId): jump to another screen in the same flow
Basic flow shape
id— unique per screen. It is thestep_idused by analytics, so two paywalls with different ids show up as two funnel rows.properties.placement— the string passed to your builder. Map it to a RevenueCat placement (or your own equivalent) to decide which paywall to show.
Navigation (soft gate)
A paywall screen never blocks the flow. Callctx.onNext() in both outcomes:
- after a successful purchase
- after the user dismisses the paywall
paywallScreenBuilder is registered, the SDK skips paywall screens entirely (no blank screen, no screen_view event for the skipped step).
Example
Platform support: paywall screens require flowboard-pckg-swift and flowboard_flutter versions that include the
paywallScreenBuilder API. React Native / Expo and Android (Kotlin) do not implement the paywall screen type yet, and older SDK versions render a paywall screen as a blank step — only add paywall screens to flows served to apps running a supporting SDK.Migrating from the rc_paywall custom screen
Previously the common pattern was a type: "custom" screen with id rc_paywall and a free-form payload { "placement": "..." }. That pattern keeps working unchanged.
The paywall type improves on it in two ways:
- Analytics — with
customScreenBuilderthe id doubles as the routing key, so every paywall ends up with the same id and Pulse merges them into a single funnel step. Paywall screens are routed by type, so each screen keeps a unique id and its own funnel row. - Typed editor UI — the dashboard edits
placementin a dedicated field instead of a raw JSON textarea.
paywallScreenBuilder alongside (or instead of) your rc_paywall branch in customScreenBuilder.