You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 04:47:04