After upgrading our Flutter environment from version 3.13.8 to 3.19.3, we encountered a persistent error when fetching offerings from RevenueCat using the purchases_flutter
plugin. The application throws a type cast exception, specifically type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
, during the offerings fetch operation.
Details
- Flutter Version: Upgraded from 3.13.8 to 3.19.3.
purchases_flutter
Version: Initially encountered on version 6.21.0; upgraded to 6.24.0 in an attempt to resolve the issue, but the error persists.- Error Message:
type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
- Error Location: The error occurs within the
try-catch
block around thePurchases.getOfferings()
call, indicating an issue with handling the fetched offerings data. - Observations:
- The debug logs indicate that the
getOfferings
call successfully contacts RevenueCat and receives a response, but fails during JSON deserialization or handling within the plugin. - The issue arose immediately following the Flutter upgrade.
- The debug logs indicate that the
Code
Future<void> updateAvailablePlans() async {
_logVerboseLevelMessage('Updating Available Plans');
purchasesUpdating.value = true;
Offerings? offerings;
try {
_logVerboseLevelMessage('Getting Offerings from RevenueCat');
offerings = await Purchases.getOfferings();
_logVerboseLevelMessage('Offerings: $offerings');
} on PlatformException catch (e) {
debugPrint('Offerings Error: $e');
Get.customSnackBar(
status: StatusType.error,
compact: true,
title: e.message ?? 'Unknown error',
);
allAvailablePlans.clear();
return;
} catch (e) {
_logErrorLevelMessage('Error getting Offerings: $e', e, null);
Get.customSnackBar(
status: StatusType.error,
compact: true,
title: e.toString(),
);
allAvailablePlans.clear();
return;
}
purchasesUpdating.value = false;
// .... Rest of the code
}
Logs
D/[Purchases] - DEBUG(25790): ℹ️ Debug logging enabled
D/[Purchases] - DEBUG(25790): ℹ️ SDK Version - 7.5.2
D/[Purchases] - DEBUG(25790): ℹ️ Package name - com.deepklarity.storia
D/[Purchases] - DEBUG(25790): 👤 Initial App User ID: {Omitted}
D/[Purchases] - DEBUG(25790): ℹ️ Purchases configured with response verification: DISABLED
D/[Purchases] - DEBUG(25790): 👤 Identifying App User ID: {Omitted}
D/[Purchases] - DEBUG(25790): ℹ️ Deleting old synced subscriber attributes that don't belong to {Omitted}
D/[Purchases] - DEBUG(25790): ℹ️ App foregrounded
D/[Purchases] - DEBUG(25790): ℹ️ CustomerInfo cache is stale, updating from network in foreground.
D/[Purchases] - DEBUG(25790): Retrieving customer info with policy: FETCH_CURRENT
D/[Purchases] - DEBUG(25790): ℹ️ Updating pending purchase queue
D/[Purchases] - DEBUG(25790): ℹ️ Offerings cache is stale, updating from network in foreground
D/[Purchases] - DEBUG(25790): 😻 Start Offerings update from network.
D/[Purchases] - DEBUG(25790): ℹ️ Querying purchases
D/[Purchases] - DEBUG(25790): Request already scheduled with jitter delay, adding existing callbacks to unjittered request with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}/offerings], appInBackground=false)
D/[Purchases] - DEBUG(25790): ℹ️ Updating pending purchase queue
D/[Purchases] - DEBUG(25790): ℹ️ No subscriber attributes to synchronize.
D/[Purchases] - DEBUG(25790): ℹ️ Listener set
D/[Purchases] - DEBUG(25790): ℹ️ Sending latest CustomerInfo to listener.
D/[Purchases] - DEBUG(25790): ℹ️ Starting connection for com.android.billingclient.api.BillingClientImpl@af4104a
D/[Purchases] - DEBUG(25790): ℹ️ Ending connection for com.android.billingclient.api.BillingClientImpl@2285499
D/[Purchases] - DEBUG(25790): ℹ️ Billing Service Setup finished for com.android.billingclient.api.BillingClientImpl@af4104a
D/[Purchases] - DEBUG(25790): ℹ️ Updating pending purchase queue
D/[Purchases] - DEBUG(25790): Retrieving customer info with policy: CACHED_OR_FETCHED
D/[Purchases] - DEBUG(25790): ℹ️ Vending CustomerInfo from cache.
D/[Purchases] - DEBUG(25790): ℹ️ Checking if cache is stale AppInBackground false
D/[Purchases] - DEBUG(25790): ℹ️ Syncing purchases
D/[Purchases] - DEBUG(25790): ℹ️ Querying purchase history for type subs
D/[Purchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ Saving tokens []
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/[Purchases] - DEBUG(25790): Request already scheduled with jitter delay, adding existing callbacks to unjittered request with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}], appInBackground=false)
D/[Purchases] - DEBUG(25790): Retrieving customer info with policy: CACHED_OR_FETCHED
D/[Purchases] - DEBUG(25790): ℹ️ Vending CustomerInfo from cache.
D/[Purchases] - DEBUG(25790): ℹ️ Checking if cache is stale AppInBackground false
D/[Purchases] - DEBUG(25790): ℹ️ No cached Offerings, fetching from network
D/[Purchases] - DEBUG(25790): 😻 Start Offerings update from network.
D/[Purchases] - DEBUG(25790): Same call already in progress, adding to callbacks map with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}/offerings], appInBackground=false)
D/[Purchases] - DEBUG(25790): Request already scheduled with jitter delay, adding existing callbacks to unjittered request with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}/offerings], appInBackground=false)
D/[Purchases] - DEBUG(25790): Billing connected with country code: IN
D/TrafficStats(25790): tagSocket(225) with statsTag=0xffffffff, statsUid=-1
W/WindowOnBackDispatcher(25790): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(25790): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
D/[Purchases] - DEBUG(25790): API request started: GET /subscribers/{Omitted}/offerings
D/[Purchases] - DEBUG(25790): API request completed with status: GET /subscribers/{Omitted}/offerings 304
2
D/[Purchases] - DEBUG(25790): ℹ️ Requesting products from the store with identifiers: storia_premium, storia_topup_con_199
2
D/[Purchases] - DEBUG(25790): ℹ️ Querying purchases
D/[Purchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ Saving tokens []
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/[Purchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ Saving tokens []
D/[Purchases] - DEBUG(25790): ℹ️ Tokens already posted: []
D/[Purchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/[Purchases] - DEBUG(25790): ℹ️ Products request finished for storia_premium, storia_topup_con_199
D/[Purchases] - DEBUG(25790): ℹ️ Requesting products from the store with identifiers: storia_topup_con_199
D/[Purchases] - DEBUG(25790): ℹ️ Products request finished for storia_topup_con_199
D/[Purchases] - DEBUG(25790): 💰 Retrieved productDetailsList: ProductDetails{jsonString='{"productId":"storia_topup_con_199","type":"inapp","title":"TopUp (Storia - AI generated stories)","name":"TopUp","description":"TopUp","localizedIn":["en-US"],"skuDetailsToken":"AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=","oneTimePurchaseOfferDetails":{"priceAmountMicros":180000000,"priceCurrencyCode":"INR","formattedPrice":"₹180.00"}}', parsedJson={"productId":"storia_topup_con_199","type":"inapp","title":"TopUp (Storia - AI generated stories)","name":"TopUp","description":"TopUp","localizedIn":["en-US"],"skuDetailsToken":"AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=","oneTimePurchaseOfferDetails":{"priceAmountMicros":180000000,"priceCurrencyCode":"INR","formattedPrice":"₹180.00"}}, productId='storia_topup_con_199', productType='inapp', title='TopUp (Storia - AI generated stories)', productDetailsToken='AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=', subscriptionOfferDetails=null}
2
D/[Purchases] - DEBUG(25790): 💰 storia_topup_con_199 - ProductDetails{jsonString='{"productId":"storia_topup_con_199","type":"inapp","title":"TopUp (Storia - AI generated stories)","name":"TopUp","description":"TopUp","localizedIn":["en-US"],"skuDetailsToken":"AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=","oneTimePurchaseOfferDetails":{"priceAmountMicros":180000000,"priceCurrencyCode":"INR","formattedPrice":"₹180.00"}}', parsedJson={"productId":"storia_topup_con_199","type":"inapp","title":"TopUp (Storia - AI generated stories)","name":"TopUp","description":"TopUp","localizedIn":["en-US"],"skuDetailsToken":"AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=","oneTimePurchaseOfferDetails":{"priceAmountMicros":180000000,"priceCurrencyCode":"INR","formattedPrice":"₹180.00"}}, productId='storia_topup_con_199', productType='inapp', title='TopUp (Storia - AI generated stories)', productDetailsToken='AEuhp4JGY4oM_SKIBDBFHhLlW741aOpnE0ho-4WBrg6sV2zuuAMgxsSjQ3rMm-iz0BM=', subscriptionOfferDetails=null}
D/[Purchases] - DEBUG(25790): ℹ️ Building offerings response with 2 products
I/flutter (25790): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter (25790): │ type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
I/flutter (25790): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (25790): │ #0 LoggerHelper.logErrorLevelMessage (package:storia/logic/services/misc_helpers/logger_helper.dart:33:13)
I/flutter (25790): │ #1 PurchasesHelper._logErrorLevelMessage (package:storia/logic/services/purchases_helpers/purchases_helper.dart:386:10)
I/flutter (25790): │ #2 PurchasesHelper.updateAvailablePlans (package:storia/logic/services/purchases_helpers/purchases_helper.dart:309:7)
I/flutter (25790): │ #3 <asynchronous suspension>
I/flutter (25790): │ #4 PurchasesHelper._customerUpdateListener (package:storia/logic/services/purchases_helpers/purchases_helper.dart:63:5)
I/flutter (25790): │ #5 <asynchronous suspension>
I/flutter (25790): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (25790): │ ⛔ Error getting Offerings: type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
I/flutter (25790): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
D/[Purchases] - DEBUG(25790): API request started: GET /subscribers/6VhC9mt2AFYPWg7puz2nqnzrbqR2
D/[Purchases] - DEBUG(25790): API request completed with status: GET /subscribers/6VhC9mt2AFYPWg7puz2nqnzrbqR2 304
D/[Purchases] - DEBUG(25790): 😻 CustomerInfo updated from network.
D/[Purchases] - DEBUG(25790): ℹ️ Purchase history is empty.
D/[Purchases] - DEBUG(25790): ℹ️ Querying purchase history for type inapp
D/[Purchases] - DEBUG(25790): ℹ️ Purchase history is empty.
D/[Purchases] - DEBUG(25790): Retrieving customer info with policy: CACHED_OR_FETCHED
D/[Purchases] - DEBUG(25790): ℹ️ Vending CustomerInfo from cache.
D/[Purchases] - DEBUG(25790): ℹ️ Checking if cache is stale AppInBackground false
D/ScrollOptim [SceneManager](25790): updateCurrentActivity: mCurrentActivityName=null, isOptEnable=true, isAnimAheadEnable=true, isFrameInsertEnable=true, InsertNum=1, isEnabledForScrollChanged=false