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(ProductRequest params)
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
params.skus | List<String> | ✅ | Store identifiers to query (Google Play product IDs / App Store product identifiers) |
params.type | ProductQueryType? | ❌ | ProductQueryType.InApp (default) or ProductQueryType.Subs |
Return Value
The method returns a FetchProductsResult union type:
FetchProductsResultProducts:
- Contains
.valueproperty withList<Product>?(nullable list) - Returned for
ProductQueryType.InAppqueries
FetchProductsResultSubscriptions:
- Contains
.valueproperty withList<ProductSubscription>?(nullable list) - Returned for
ProductQueryType.Subsqueries
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(
ProductRequest(
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(
ProductRequest(
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(
ProductRequest(skus: ['coins_100', 'remove_ads'], type: ProductQueryType.InApp),
);
final subsResult = await FlutterInappPurchase.instance.fetchProducts(
ProductRequest(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(
ProductRequest(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
- Replace
requestProducts()calls withfetchProducts(ProductRequest(...)) - Convert the returned
FetchProductsResultusinginAppProducts(),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