Feature SDKs
How to add a paywall and subscription state to a Flutter app
Free Reference for paywall_sdk: every variable, option and package it generates. Command: archipelago generate paywall_sdk
Generate the Paywall SDK, use the mock purchase flow, check premium status and listen for changes, and swap in a real payment provider.
What you'll learn
- Generating the Paywall SDK with a pricing page and entitlements
- Understanding the mock purchase flow
- Checking premium status and listening for changes
- Replacing the mock with a real payment provider
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
The paywall does not depend on auth_sdk. The brick generates core_infrastructure_package for you if it is missing. It reads your UI kit name from archipelago.yaml.
Step 1: Generate the Paywall SDK
archipelago generate paywall_sdkThe prompts are:
- appName: your app name, for example
MyApp - prefix: the prefix for generated classes
- uiKitPackageName: read from
archipelago.yaml - isForMonorepo: leave this as
true - stateManagement:
bloc(default),cubit,riverpodorprovider
Or use a config file:
{
"appName": "MyApp",
"isForMonorepo": true,
"stateManagement": "bloc"
}archipelago generate paywall_sdk --config paywall_config.jsonStep 2: Understand the Generated Structure
features/paywall/
├── paywall_api/
│ └── lib/src/ # PaywallSDK, events, products, purchase result, status, tier
└── paywall_impl/
└── lib/src/
├── paywall_sdk_impl.dart
├── data/ # PaywallDataSource (mock) and the repository
├── domain/usecases/
├── di/ # Global module, container, imports
├── presentation/
│ ├── paywall_shell_page.dart
│ ├── paywall_page.dart
│ └── bloc/ # Or cubit or notifiers, per stateManagement
└── router/paywall_router.dartStep 3: Check the Registration
The post-generation hook registers PaywallImplGlobalModule in lib/di/injector.dart. You write no registration code.
- The global module registers
PaywallManifest(the/paywallroutes) andPaywallSDKas a lazy singleton. The status starts asfree. - The data source, repository and use cases live in
paywallContainer, a child GetIt.paywall_imports.dartforwards the rootPaywallSDK. - The shell page builds the state holder from its lease.
PaywallPagenever reads GetIt.
Step 4: Check Entitlements and Open the Paywall
final paywall = getIt<PaywallSDK>();
if (paywall.isPremium) {
// Pro or enterprise access
}
if (paywall.hasEntitlement('export_pdf')) {
// Show the feature
}
await paywall.launchPaywall(context);Listen for changes:
final remove = paywall.addStatusListener((event) {
switch (event) {
case PaywallPurchaseEvent(): refreshEntitlements();
case PaywallRestoreEvent(): syncData();
case PaywallExpiredEvent(): showRenewalPrompt();
case PaywallStatusChangedEvent(): updateUi();
}
});statusStream, currentStatus and currentTier give the same data without a listener.
Step 5: Use the Mock Purchase Flow
MockPaywallDataSource returns four fixed products: pro_monthly, pro_yearly, enterprise_monthly and enterprise_yearly. Its purchase() always succeeds with the transaction ID mock_txn_123. No store, receipt check or server sync exists.
final products = await paywall.getProducts();
final result = await paywall.purchase(products.first);Treat the generated feature as UI and flow scaffolding. It is not a production paywall.
Step 6: Connect a Real Payment Provider
Implement PaywallDataSource with your provider and register it in place of the mock in the feature container. Keep the same method shapes:
@LazySingleton(as: PaywallDataSource)
class RevenueCatPaywallDataSource implements PaywallDataSource {
@override
Future<List<PaywallProduct>> getProducts() async {
// Map your provider's offerings to PaywallProduct
}
@override
Future<PurchaseResult> purchase(PaywallProduct product) async {
// Call the store SDK and map the result
}
}Validate receipts and sync entitlements on your server. The mock does neither.
Step 7: Run the Tests
cd features/paywall/paywall_impl
flutter testpaywall_di_test.dart checks the manifest, the lazy SDK and the container. The presentation tests cover the chosen state holder.
Common Customizations
| Customization | Where to Change |
|---|---|
| Products and plans | The data source |
| Paywall layout | presentation/paywall_page.dart |
| Entitlement rules | PaywallSDKImpl |
| Gate a feature | Call hasEntitlement or isPremium in the feature |
Next Steps
- Set up the Home SDK with subscription-gated content
- Configure Monitoring to track purchase failures