Flutter集成RevenueCat遇空检查运算符错误,求排查原因
Flutter集成RevenueCat空检查运算符错误排查
问题详情
首次搭建Flutter应用并集成RevenueCat时,触发以下异常:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
怀疑错误出在这段代码中:
PurchasesConfiguration configuration; print('2'); configuration = PurchasesConfiguration(StoreConfig.instance!.apiKey) ..appUserID = null ..observerMode = false;
错误截图:
启动页(SplashScreen)代码
class SplashScreen extends StatefulWidget { @override _SplashScreenState createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> { late Timer timer; @override void initState() { super.initState(); timer = Timer(const Duration(seconds: 10), () { print('timer'); goToMain(); }); print('start'); SubscriptionsProvider.shared.addListener(() async { if (!mounted) { return; } if (SubscriptionsProvider.shared.entitlementIsActive) { goToMain(); } else { print('else'); try { SubscriptionsProvider.shared.subs = await Purchases.getProducts([sub1Id, sub2Id, sub3Id]); } on PlatformException catch (e) { print('error'); goToMain(); } if (SubscriptionsProvider.shared.subs == null || SubscriptionsProvider.shared.subs!.isEmpty) { goToMain(); } else { goToPayWall(); } } }); // initPlatformState(); } goToMain() { if (mounted) { Navigator.of(context) .pushReplacement(MaterialPageRoute(builder: (context) => Mainpage())); } } goToPayWall() { if (mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => PayWallRevenueCat())); } } @override void dispose() { timer.cancel(); SubscriptionsProvider.shared.removeListener(() {}); super.dispose(); } @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xff000000), Color(0xff004BA4)])), child: const Scaffold(body: Center(child: CircularProgressIndicator()))); } }
SubscriptionsProvider代码
class SubscriptionsProvider extends ChangeNotifier { static SubscriptionsProvider shared = SubscriptionsProvider._(); bool entitlementIsActive = false; String appUserID = ''; List<StoreProduct>? subs; SubscriptionsProvider._() { initPlatformState(); } initPlatformState() async { // Enable debug logs before calling `configure`. print('1'); await Purchases.setLogLevel(LogLevel.debug); PurchasesConfiguration configuration; print('2'); configuration = PurchasesConfiguration(StoreConfig.instance!.apiKey) ..appUserID = null ..observerMode = false; await Purchases.configure(configuration); print('3'); appUserID = await Purchases.appUserID; print('4'); Purchases.addCustomerInfoUpdateListener((customerInfo) async { print('Purchases.addCustomerInfoUpdateListener started'); appUserID = await Purchases.appUserID; CustomerInfo customerInfo = await Purchases.getCustomerInfo(); (customerInfo.entitlements.all[entitlementID] != null && customerInfo.entitlements.all[entitlementID]!.isActive) ? entitlementIsActive = true : entitlementIsActive = false; notifyListeners(); }); } }
问题原因
异常直接指向StoreConfig.instance!——StoreConfig.instance为null,使用!强制解包触发了空值异常。
解决步骤
1. 确保StoreConfig提前初始化
StoreConfig的单例必须在SubscriptionsProvider初始化前完成初始化,建议在main()函数中优先处理:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 替换为你的RevenueCat API密钥 StoreConfig.initialize(apiKey: 'your_revenuecat_api_key'); runApp(MyApp()); }
同时检查StoreConfig的单例实现是否正确,示例实现如下:
class StoreConfig { static StoreConfig? _instance; final String apiKey; StoreConfig._({required this.apiKey}); static void initialize({required String apiKey}) { _instance = StoreConfig._(apiKey: apiKey); } static StoreConfig get instance { if (_instance == null) { throw StateError('请先调用StoreConfig.initialize初始化配置'); } return _instance!; } }
2. 增加空安全判断,避免强制解包
即使初始化逻辑正确,也建议添加空判断防止意外崩溃:
initPlatformState() async { print('1'); await Purchases.setLogLevel(LogLevel.debug); final storeConfig = StoreConfig.instance; // 增加空判断 if (storeConfig == null) { print('StoreConfig未初始化,终止RevenueCat配置'); entitlementIsActive = false; notifyListeners(); return; } PurchasesConfiguration configuration = PurchasesConfiguration(storeConfig.apiKey) ..appUserID = null ..observerMode = false; await Purchases.configure(configuration); // 后续代码... }
3. 调整初始化时机
SubscriptionsProvider.shared是静态单例,首次访问时会触发构造函数调用initPlatformState。而你在SplashScreen的initState中直接访问了该单例,因此必须保证StoreConfig的初始化在runApp之前完成,否则会出现空值。
额外优化建议
- 在
initPlatformState中添加try-catch包裹RevenueCat初始化逻辑,避免异常导致启动流程崩溃:
initPlatformState() async { try { print('1'); await Purchases.setLogLevel(LogLevel.debug); final storeConfig = StoreConfig.instance; if (storeConfig == null) { print('StoreConfig未初始化'); entitlementIsActive = false; notifyListeners(); return; } PurchasesConfiguration configuration = PurchasesConfiguration(storeConfig.apiKey) ..appUserID = null ..observerMode = false; await Purchases.configure(configuration); print('3'); appUserID = await Purchases.appUserID; print('4'); Purchases.addCustomerInfoUpdateListener((customerInfo) async { print('Purchases.addCustomerInfoUpdateListener started'); appUserID = await Purchases.appUserID; CustomerInfo customerInfo = await Purchases.getCustomerInfo(); // 简化空判断写法 entitlementIsActive = customerInfo.entitlements.all[entitlementID]?.isActive ?? false; notifyListeners(); }); } catch (e) { print('RevenueCat初始化失败: $e'); entitlementIsActive = false; notifyListeners(); } }
内容的提问来源于stack exchange,提问作者skv500
相关产品推荐
相关产品推荐

