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

Flutter集成in_app_purchase包触发java.lang.NullPointerException求助

Flutter集成in_app_purchase包出现java.lang.NullPointerException问题排查

我在Flutter项目中集成in_app_purchase包(用于对接自有后端验证购买),但运行后出现java.lang.NullPointerException错误,控制台仅输出:

W/BillingLogger( 7906): Unable to log.
W/BillingLogger( 7906): java.lang.NullPointerException

相关代码如下:

class SubscriptionPage extends StatefulWidget {
  const SubscriptionPage({super.key});

  @override
  State<SubscriptionPage> createState() => _SubscriptionPageState();
}

class _SubscriptionPageState extends State<SubscriptionPage> {
  final InAppPurchase _inAppPurchase = InAppPurchase.instance;
  late StreamSubscription<List<PurchaseDetails>> _subscription;

  //Product Listing
  static const List<String> _productIds = [
    'com.....subscription'
  ];

  @override
  void initState() {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subscription =
        purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
      if (mounted) {
        _listenToPurchaseUpdated(purchaseDetailsList, context);
      }
    }, onDone: () {
      _subscription.cancel();
    }, onError: (Object error) {
      if (mounted) {
        showSnackBar(context, 'Something went wrong', Severity.error);
      }
    });
    super.initState();
  }

  Future<Subscription> getProducts() async {
    final bool isAvailable = await _inAppPurchase.isAvailable();
    if (!isAvailable) {
      return Subscription(
        isAvailable: isAvailable,
        products: [],
        notFoundIds: [],
      );
    }
    final ProductDetailsResponse productDetailResponse =
        await _inAppPurchase.queryProductDetails(_productIds.toSet());

    if (productDetailResponse.error != null) {
      throw Exception(productDetailResponse.error!.message);
    }
    return Subscription(
      isAvailable: isAvailable,
      products: productDetailResponse.productDetails,
      notFoundIds: productDetailResponse.notFoundIDs,
    );
  }

  @override
  void dispose() {
    _subscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var media = MediaQuery.of(context).size;
    return Scaffold(
      appBar: const BasicAppBar(
        title: 'Subscription',
        isBack: true,
        level: '3',
      ),
      body: Column(
        children: [
          //.....
        ],
      ),
    );
  }

  _listenToPurchaseUpdated(
      List<PurchaseDetails> purchaseDetailsList, BuildContext context) async {
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.status == PurchaseStatus.pending) {
        showSnackBar(
          context,
          'You are purchasing a subscription',
          Severity.success,
        );
      } else if (purchaseDetails.status == PurchaseStatus.error) {
        showSnackBar(
          context,
          'Your purchased failed!',
          Severity.error,
        );
      } else if (purchaseDetails.status == PurchaseStatus.purchased) {
        showSnackBar(
          context,
          'Your purchase is successfully',
          Severity.success,
        );
      }
    }
  }
}

排查与解决建议

  • 强制指定Google Billing库版本
    in_app_purchase底层依赖Google Play Billing库,版本不匹配可能触发空指针。在android/app/build.gradle中明确指定兼容版本:

    dependencies {
        implementation 'com.android.billingclient:billing:6.1.0'
    }
    
  • 调整初始化时机
    避免在initState中直接订阅流,先确认服务可用再初始化:

    @override
    void initState() {
      super.initState();
      _initPurchaseStream();
    }
    
    Future<void> _initPurchaseStream() async {
      final bool isAvailable = await _inAppPurchase.isAvailable();
      if (isAvailable && mounted) {
        final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
        _subscription = purchaseUpdated.listen(
          (List<PurchaseDetails> purchaseDetailsList) {
            _listenToPurchaseUpdated(purchaseDetailsList, context);
          },
          onDone: () {
            if (mounted) _subscription.cancel();
          },
          onError: (Object error) {
            if (mounted) showSnackBar(context, 'Something went wrong', Severity.error);
          },
        );
      }
    }
    
  • 校验产品ID与空值处理
    确保_productIds与Google Play Console中配置的订阅ID完全一致;同时在购买监听逻辑中增加空值判断:

    void _listenToPurchaseUpdated(
        List<PurchaseDetails> purchaseDetailsList, BuildContext context) {
      for (final PurchaseDetails? purchaseDetails in purchaseDetailsList) {
        if (purchaseDetails == null) continue;
        // 后续状态判断逻辑
      }
    }
    
  • 清理构建缓存
    执行以下命令清理缓存后重新构建项目:

    flutter clean
    flutter pub get
    cd android && ./gradlew clean && cd ..
    

内容的提问来源于stack exchange,提问作者user25770779

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 12:45:19