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/DPurchases] - DEBUG(25790): ℹ️ Debug logging enabled
D/rPurchases] - DEBUG(25790): ℹ️ SDK Version - 7.5.2
D/.Purchases] - DEBUG(25790): ℹ️ Package name - com.deepklarity.storia
D/sPurchases] - DEBUG(25790): 👤 Initial App User ID: {Omitted}
D/{Purchases] - DEBUG(25790): ℹ️ Purchases configured with response verification: DISABLED
D/iPurchases] - DEBUG(25790): 👤 Identifying App User ID: {Omitted}
D/ePurchases] - 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/oPurchases] - 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/OPurchases] - DEBUG(25790): ℹ️ Updating pending purchase queue
D/(Purchases] - DEBUG(25790): ℹ️ No subscriber attributes to synchronize.
D/0Purchases] - DEBUG(25790): ℹ️ Listener set
D/ D/BPurchases] - DEBUG(25790): ℹ️ Starting connection for com.android.billingclient.api.BillingClientImpl@af4104a
D/iPurchases] - DEBUG(25790): ℹ️ Ending connection for com.android.billingclient.api.BillingClientImpl@2285499
D/nPurchases] - DEBUG(25790): ℹ️ Billing Service Setup finished for com.android.billingclient.api.BillingClientImpl@af4104a
D/uPurchases] - DEBUG(25790): ℹ️ Updating pending purchase queue
D/aPurchases] - DEBUG(25790): Retrieving customer info with policy: CACHED_OR_FETCHED
D/ePurchases] - DEBUG(25790): ℹ️ Vending CustomerInfo from cache.
D/CPurchases] - DEBUG(25790): ℹ️ Checking if cache is stale AppInBackground false
D/[Purchases] - DEBUG(25790): ℹ️ Syncing purchases
D/sPurchases] - DEBUG(25790): ℹ️ Querying purchase history for type subs
D/pPurchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D/hPurchases] - DEBUG(25790): ℹ️ Tokens already posted: )]
D/CPurchases] - DEBUG(25790): ℹ️ Saving tokens a]
D/BPurchases] - DEBUG(25790): ℹ️ Tokens already posted: u]
D/-Purchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/2Purchases] - DEBUG(25790): Request already scheduled with jitter delay, adding existing callbacks to unjittered request with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}], appInBackground=false)
D/rPurchases] - DEBUG(25790): Retrieving customer info with policy: CACHED_OR_FETCHED
D/pPurchases] - DEBUG(25790): ℹ️ Vending CustomerInfo from cache.
D/oPurchases] - DEBUG(25790): ℹ️ Checking if cache is stale AppInBackground false
D/iPurchases] - DEBUG(25790): ℹ️ No cached Offerings, fetching from network
D/fPurchases] - 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/tPurchases] - DEBUG(25790): Request already scheduled with jitter delay, adding existing callbacks to unjittered request with key: BackgroundAwareCallbackCacheKey(cacheKey=[/subscribers/{Omitted}/offerings], appInBackground=false)
D/jPurchases] - 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/pPurchases] - DEBUG(25790): API request started: GET /subscribers/{Omitted}/offerings
D/uPurchases] - DEBUG(25790): API request completed with status: GET /subscribers/{Omitted}/offerings 304
2
D/fPurchases] - DEBUG(25790): ℹ️ Requesting products from the store with identifiers: storia_premium, storia_topup_con_199
2
D/PPurchases] - DEBUG(25790): ℹ️ Querying purchases
D/mPurchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D//Purchases] - DEBUG(25790): ℹ️ Tokens already posted: ]
D/ D/vPurchases] - DEBUG(25790): ℹ️ Tokens already posted: ]
D/kPurchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/aPurchases] - DEBUG(25790): ℹ️ Cleaning previously sent tokens
D/dPurchases] - DEBUG(25790): ℹ️ Tokens already posted: ]
D/nPurchases] - DEBUG(25790): ℹ️ Saving tokens U]
D/ Purchases] - DEBUG(25790): ℹ️ Tokens already posted: s]
D/UPurchases] - DEBUG(25790): ℹ️ No pending purchases to sync
D/BPurchases] - DEBUG(25790): ℹ️ Products request finished for storia_premium, storia_topup_con_199
D/rPurchases] - DEBUG(25790): ℹ️ Requesting products from the store with identifiers: storia_topup_con_199
D/PPurchases] - DEBUG(25790): ℹ️ Products request finished for storia_topup_con_199
D/EPurchases] - DEBUG(25790): 💰 Retrieved productDetailsList: ProductDetails{jsonString='{"productId":"storia_topup_con_199","type":"inapp","title":"TopUp (Storia - AI generated stories)","name":"TopUp","description":"TopUp","localizedIn":p"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":I"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/uPurchases] - 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":C"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/9Purchases] - 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/sPurchases] - DEBUG(25790): API request completed with status: GET /subscribers/6VhC9mt2AFYPWg7puz2nqnzrbqR2 304
D/tPurchases] - DEBUG(25790): 😻 CustomerInfo updated from network.
D/7Purchases] - 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 ySceneManager](25790): updateCurrentActivity: mCurrentActivityName=null, isOptEnable=true, isAnimAheadEnable=true, isFrameInsertEnable=true, InsertNum=1, isEnabledForScrollChanged=false