There is no singleton instance, even if it is configured
- [x] I have updated Purchases SDK to the latest version
- [x] I have read the Contribution Guidelines
- [x] I have searched the Community
- [x] I have read docs.revenuecat.com
- [x] I have searched for existing Github issues
‼️ Required data ‼️
Do not remove any of the steps from the template below. If a step is not applicable to your issue, please leave that step empty.
There are a lot of things that can contribute to things not working. Having a very basic understanding of your environment will help us understand your issue faster!
Environment
- [x] Output of
flutter doctor[✓] Flutter (Channel stable, 3.24.3, on macOS 14.6.1 23G93 darwin-arm64, locale pl-PL) [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 16.0) [✓] Chrome - develop for the web [!] Android Studio (version unknown) ✗ Unable to determine Android Studio version. [✓] VS Code (version 1.94.1) [✓] Connected device (5 available) [✓] Network resources - [x] Version of
purchases-flutter: 8.1.6,purchases-ui-flutter: 8.1.6 - [ ] Testing device version e.g.: different versions of Android
- [x] How often the issue occurs- every one of your customers is impacted? Only in dev? It is in production version. Not all users
- [x] Debug logs that reproduce the issue
- [ ] Steps to reproduce, with a description of expected vs. actual behavior Other information (e.g. stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, etc.)
Describe the bug
RevenueCat Paywall make sometimes error:
Exception wf.f0: There is no singleton instance. Make sure you configure Purchases before trying to get the default instance. More info here: https://errors.rev.cat/configuring-sdk
at com.revenuecat.purchases.Purchases$Companion.getSharedInstance (Purchases.java)
at com.revenuecat.purchases.ui.revenuecatui.data.PurchasesImpl.<init> (PurchasesImpl.java:3)
at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelImpl.<init> (PaywallViewModelImpl.java:2)
at com.revenuecat.purchases.ui.revenuecatui.data.PaywallViewModelFactory.create (PaywallViewModelFactory.java:1)
at androidx.lifecycle.ViewModelProvider$Factory.create (ViewModelProvider.java:2)
at androidx.lifecycle.ViewModelProvider.get (ViewModelProvider.java:1)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get (ViewModel.kt:1)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel (ViewModel.kt:1)
at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.getPaywallViewModel (InternalPaywall.kt:1)
at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.InternalPaywall (InternalPaywall.kt:1)
at com.revenuecat.purchases.ui.revenuecatui.PaywallKt.Paywall (Paywall.kt:1)
at com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivity$onCreate$1$1$1.invoke (PaywallActivity.java:2)
at com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivity$onCreate$1$1$1.invoke (PaywallActivity.java:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:2)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke (Scaffold.kt:2)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke (Scaffold.kt:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke (LayoutNodeSubcompositionsState.java:2)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke (ComposableLambdaImpl.java:1)
at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable (ActualJvm_jvm.kt:1)
at androidx.compose.runtime.ComposerImpl.doCompose (ComposerImpl.java:1)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release (ComposerImpl.java:1)
at androidx.compose.runtime.CompositionImpl.composeContent (CompositionImpl.java:1)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release (Recomposer.java:1)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release (ComposerImpl.java:1)
at androidx.compose.runtime.CompositionImpl.composeInitial (CompositionImpl.java:1)
at androidx.compose.runtime.CompositionImpl.setContent (CompositionImpl.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke-0kLqBqw (Scaffold.kt:1)
at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke (Scaffold.kt:1)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s (LayoutNodeSubcompositionsState.java:1)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.foundation.layout.BoxMeasurePolicy.measure-3p2s80s (BoxMeasurePolicy.java:1)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s (SimpleGraphicsLayerModifier.java:1)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0 (LayoutModifierNodeCoordinator.java:1)
at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s (SimpleGraphicsLayerModifier.java:1)
at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0 (LayoutModifierNodeCoordinator.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s (RootMeasurePolicy.java:1)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0 (InnerNodeCoordinator.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:2)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe (SnapshotStateObserver.java:1)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads (SnapshotStateObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release (OwnerSnapshotObserver.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0 (LayoutNodeLayoutDelegate.java:1)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release (LayoutNode.java:1)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA (MeasureAndLayoutDelegate.java:1)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureOnly (MeasureAndLayoutDelegate.java:1)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureOnly (MeasureAndLayoutDelegate.java:1)
at androidx.compose.ui.platform.AndroidComposeView.onMeasure (AndroidComposeView.java:1)
at android.view.View.measure (View.java:28571)
at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release (AbstractComposeView.java:1)
at androidx.compose.ui.platform.AbstractComposeView.onMeasure (AbstractComposeView.java:1)
at android.view.View.measure (View.java:28571)
at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
at android.widget.FrameLayout.onMeasure (FrameLayout.java:194)
at android.view.View.measure (View.java:28571)
at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
at android.widget.LinearLayout.measureChildBeforeLayout (LinearLayout.java:1608)
at android.widget.LinearLayout.measureVertical (LinearLayout.java:878)
at android.widget.LinearLayout.onMeasure (LinearLayout.java:721)
at android.view.View.measure (View.java:28571)
at android.view.ViewGroup.measureChildWithMargins (ViewGroup.java:7390)
at android.widget.FrameLayout.onMeasure (FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure (DecorView.java:1036)
at android.view.View.measure (View.java:28571)
at android.view.ViewRootImpl.performMeasure (ViewRootImpl.java:5203)
at android.view.ViewRootImpl.measureHierarchy (ViewRootImpl.java:3603)
at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3973)
at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:3288)
at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:11344)
at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1689)
at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1698)
at android.view.Choreographer.doCallbacks (Choreographer.java:1153)
at android.view.Choreographer.doFrame (Choreographer.java:1079)
at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1646)
at android.os.Handler.handleCallback (Handler.java:958)
at android.os.Handler.dispatchMessage (Handler.java:99)
at android.os.Looper.loopOnce (Looper.java:230)
at android.os.Looper.loop (Looper.java:319)
at android.app.ActivityThread.main (ActivityThread.java:8919)
at java.lang.reflect.Method.invoke
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:578)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1103)
Additional context
I configure RevenueCat in main with this:
Future<void> configure() async {
PurchasesConfiguration configuration;
if (Platform.isAndroid) {
configuration = PurchasesConfiguration(googleApiKey)..appUserID = null
..purchasesAreCompletedBy = const PurchasesAreCompletedByRevenueCat();
} else if (Platform.isIOS) {
configuration = PurchasesConfiguration(appleApiKey)..appUserID = null
..purchasesAreCompletedBy = const PurchasesAreCompletedByRevenueCat();
} else {
throw UnsupportedError('Unsupported platform');
}
await Purchases.configure(configuration);
}
and when I'm showing paywall I use that code:
Future<bool> presentPaywall(
BuildContext context, String previousScreen) async {
await checkPurchaseConfiguration();
bool purchaseOrRestoreSuccess = false;
final completer = Completer<bool>();
CustomerInfo customerInfo = await Purchases.getCustomerInfo();
if (customerInfo.entitlements.all[entitlementID]?.isActive != true) {
final remoteConfigHelper = await RemoteConfigHelper.getInstance();
await remoteConfigHelper.updateConfigIfNeeded();
if (remoteConfigHelper.showRevenueCatPaywall(previousScreen)) {
try {
await context.router.push(
ParentalGateRoute(
onVerified: () async {
final paywallResult = await RevenueCatUI.presentPaywall(
displayCloseButton: true,
);
FirebaseAnalytics.instance.logEvent(
name: 'view_rc_subscription_screen',
parameters: {
'timestamp': DateTime.now().toIso8601String(),
'previous_screen': previousScreen ?? 'none',
},
);
if (paywallResult == PaywallResult.purchased ||
paywallResult == PaywallResult.restored) {
appData.updatePremiumStatus(ref, true);
FirebaseAnalytics.instance.logEvent(
name: 'trial_activated_rc',
parameters: {
'timestamp': DateTime.now().toIso8601String(),
'paywallResult': paywallResult.toString(),
'previous_screen': previousScreen ?? 'none',
},
);
purchaseOrRestoreSuccess = true;
}
// Mark the completer as complete after handling the paywall result
completer.complete(purchaseOrRestoreSuccess);
},
),
);
} catch (e) {
// If there's an error (e.g., network issue), log it and fall back to the custom subscription screen
print("Error while fetching CustomerInfo or RevenueCat paywall : $e");
FirebaseCrashlytics.instance.recordError(e, null);
// Fallback: Show your own subscription screen if there's a network error or other issue
bool? result = await context.router.push<bool>(
SubscriptionRoute(previousScreen: previousScreen),
);
purchaseOrRestoreSuccess = result ?? false;
completer.complete(purchaseOrRestoreSuccess);
}
} else {
bool? result = await context.router.push<bool>(
SubscriptionRoute(previousScreen: previousScreen),
);
purchaseOrRestoreSuccess = result ?? false;
completer.complete(purchaseOrRestoreSuccess);
// }
}
} else {
// Complete the completer if the user already has premium active
completer.complete(false);
}
// Wait for the paywall process to finish before returning
return completer.future;
}
}
Future<void> checkPurchaseConfiguration() async {
bool isConfigured = await Purchases.isConfigured;
if (!isConfigured) {
PurchasesConfiguration configuration;
if (Platform.isAndroid) {
configuration = PurchasesConfiguration(googleApiKey);
} else {
configuration = PurchasesConfiguration(appleApiKey);
}
await Purchases.configure(configuration);
}
}
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
Hi @hirosz, thank you for reporting this and sharing the relevant code. Is your paywall possibly shown in a different snippet than the one you shared? For example, are you calling RevenueCatUI.presentPaywall anywhere else, that possibly isn't doing the isConfigured check?
Also, in your main flow, how quickly is your paywall shown after configuring the RevenueCat SDK? For example, after the configure block, is the paywall immediately shown afterwards? Or is there a delay in attempting to show it?
Hi! I use RevenueCatUI.presentPaywall only in that one place. I use configure in main and show paywall much later, depending on the user need to see first-time instructions.
@hirosz Can you upgrade to the latest version of the Flutter SDK? We recently fixed a configuration issue like the one you were experiencing. I'm going to close this for now, but please re-open it if, after upgrading, you continue to experience this. Thanks!