Subscription Flow
Source Code: subscription_flow_screen.dart
This example demonstrates how to implement subscription purchases with support for upgrades, downgrades, and subscription management.
Key Features
- Fetch subscription products
- Handle subscription purchases
- Check active subscriptions
- Manage subscription upgrades/downgrades (Android)
- Listen to subscription events
Implementation Overview
1. Set Up Listeners
StreamSubscription<Purchase>? _purchaseUpdatedSubscription;
StreamSubscription<PurchaseError>? _purchaseErrorSubscription;
void _setupListeners() {
_purchaseUpdatedSubscription = iap.purchaseUpdatedListener.listen(
(purchase) {
_handlePurchase(purchase);
},
);
_purchaseErrorSubscription = iap.purchaseErrorListener.listen(
(error) {
_handleError(error);
},
);
}
2. Fetch Subscription Products
final result = await iap.fetchProducts(
ProductRequest(
skus: ['monthly_sub', 'yearly_sub'],
type: ProductQueryType.Subs,
),
);
if (result is FetchProductsResultSubscriptions) {
final subscriptions = result.value ?? [];
for (final product in subscriptions) {
if (product is ProductSubscriptionIOS) {
debugPrint('iOS Subscription: ${product.displayName}');
} else if (product is ProductSubscriptionAndroid) {
debugPrint('Android Subscription: ${product.title}');
}
}
}
3. Check Active Subscriptions
// Lightweight check - returns only subscription IDs and expiry dates
final summaries = await iap.getActiveSubscriptions(['monthly_sub']);
if (summaries.isNotEmpty) {
debugPrint('Active subscription: ${summaries.first.productId}');
debugPrint('Expires: ${summaries.first.expirationDateIOS}');
}
// Detailed check - returns full purchase objects
final purchases = await iap.getAvailablePurchases(
PurchaseOptions(onlyIncludeActiveItemsIOS: true),
);
4. Purchase Subscription
await iap.requestPurchase(
RequestPurchaseProps.subs((
ios: RequestPurchaseIosProps(
sku: 'monthly_sub',
quantity: 1,
),
android: RequestPurchaseAndroidProps(
skus: ['monthly_sub'],
),
useAlternativeBilling: null,
)),
);
5. Upgrade/Downgrade Subscription (Android)
await iap.requestPurchase(
RequestPurchaseProps.subs((
ios: RequestPurchaseIosProps(
sku: 'yearly_sub',
quantity: 1,
),
android: RequestPurchaseAndroidProps(
skus: ['yearly_sub'],
replacementModeAndroid: AndroidReplacementMode.withTimeProration,
),
useAlternativeBilling: null,
)),
);
Android Replacement Modes
enum AndroidReplacementMode {
withTimeProration, // 1: Credit unused time towards new subscription
chargeProratedPrice, // 2: Charge prorated price immediately
withoutProration, // 3: No credit for unused time
deferred, // 4: New subscription starts at next renewal
chargeFullPrice, // 5: Charge full price immediately
}
Best Practices
- Use lightweight checks - Use
getActiveSubscriptions()
for quick status checks - Verify server-side - Always validate subscription status on your backend
- Handle upgrades properly - Choose appropriate replacement mode for Android
- Check subscription status - Regularly verify subscription validity
- Handle expiration - Monitor subscription expiry dates and renewal status