fetchProducts()
fetchProducts()
is the unified way to load in-app products or subscriptions from the store, consolidating the legacy helpers into a single API.
Signature
Future<FetchProductsResult> fetchProducts({
required List<String> skus,
ProductQueryType? type,
})
Parameters
Parameter | Type | Required | Description |
---|---|---|---|
skus | List<String> | ✅ | Store identifiers to query (Google Play product IDs / App Store product identifiers) |
type | ProductQueryType? | ❌ | ProductQueryType.InApp (default) or ProductQueryType.Subs |
Return Value
The method returns a FetchProductsResult
union type:
FetchProductsResultProducts:
- Contains
.value
property withList<Product>?
(nullable list) - Returned for
ProductQueryType.InApp
queries
FetchProductsResultSubscriptions:
- Contains
.value
property withList<ProductSubscription>?
(nullable list) - Returned for
ProductQueryType.Subs
queries
Important: Always access the product list via the .value
property:
final result = await fetchProducts(...);
final products = result.value ?? []; // Handle nullable list
Usage Examples
Fetch in-app products
final result = await FlutterInappPurchase.instance.fetchProducts(
skus: ['coins_100', 'remove_ads'],
type: ProductQueryType.InApp,
);
for (final product in result.value) {
print('${product.title}: ${product.displayPrice}');
}
Fetch subscriptions
final result = await FlutterInappPurchase.instance.fetchProducts(
skus: ['premium_monthly', 'premium_yearly'],
type: ProductQueryType.Subs,
);
for (final sub in result.value) {
print('${sub.title}: ${sub.displayPrice}');
print('Period: ${sub.subscriptionPeriodUnitIOS ?? sub.subscriptionInfoAndroid?.billingPeriod}');
}
Fetch both types
Future<void> loadCatalog() async {
final inAppsResult = await FlutterInappPurchase.instance.fetchProducts(
skus: ['coins_100', 'remove_ads'],
type: ProductQueryType.InApp,
);
final subsResult = await FlutterInappPurchase.instance.fetchProducts(
skus: ['premium_monthly'],
type: ProductQueryType.Subs,
);
final allProducts = [
...inAppsResult.value ?? [],
...subsResult.value ?? [],
];
for (final item in allProducts) {
debugPrint('Loaded ${item.id} (${item.displayPrice})');
}
}
Error Handling
try {
final result = await FlutterInappPurchase.instance.fetchProducts(
skus: productIds,
type: ProductQueryType.InApp,
);
if (result.value.isEmpty) {
debugPrint('No products returned for: $productIds');
}
if (result.invalidProductIds.isNotEmpty) {
debugPrint('Invalid product IDs: ${result.invalidProductIds}');
}
} on PurchaseError catch (error) {
switch (error.code) {
case 'E_NOT_PREPARED':
debugPrint('Call initConnection() before fetching products');
break;
case 'E_NETWORK_ERROR':
debugPrint('Network error – prompt the user to retry');
break;
default:
debugPrint('Unexpected store error: ${error.message}');
}
}
Platform Notes
iOS
- Products must be visible in App Store Connect (Ready for Sale or Approved)
- The bundle identifier must match the app querying the products
- StoreKit 2 caching means results may be served from a local cache on subsequent calls
Android
- Products must be active in Google Play Console (Internal Testing track or above)
- Billing client v8 requires the app to be signed with a certificate added to Play Console
- Only SKUs that belong to the current package can be queried
Migration Notes
From v6.x to v7.0
// Before (v6.x)
final result = await iap.fetchProducts(
ProductRequest(
skus: ['product_id'],
type: ProductQueryType.InApp,
),
);
// After (v7.0)
final result = await iap.fetchProducts(
skus: ['product_id'],
type: ProductQueryType.InApp,
);
From legacy APIs
- Replace
requestProducts()
calls withfetchProducts()
- Convert the returned
FetchProductsResult
usinginAppProducts()
,subscriptionProducts()
, orallProducts()
depending on the query type - Replace any legacy product-loading helpers in your codebase with
fetchProducts()
Related APIs
requestPurchase()
– Start the purchase flowgetAvailablePurchases()
– Restore owned itemsfetchProducts()
helper extensions
See Also
- Products Guide – Designing product catalogs
- Subscriptions Guide – Subscription-specific flows
- Troubleshooting – Diagnostics for common failures