我尝试将 in_app_purchase 与 Riverpod 一起使用。因此,我以 pub.dev 中的示例为例,并尝试使用 Riverpod 和 StateNotifier 来适应我的情况,但我有这个错误:LateInitializationError: Field '_instance@579396890' has not been initialized 我不知道如何修复它。
主要飞镖:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:inapppayement/subscription_provider.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(ProviderScope(child:MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.read(inAppPurchaseProvider.notifier).init();
late InAppPurchaseClass inApp=ref.watch(inAppPurchaseProvider);
return MaterialApp(
title: 'Flutter InAppPurchase TEST',
home:Scaffold(
body: Center(
child: (inApp.isAvailable) ? Column(
children: [
if (inApp.loading ) const LinearProgressIndicator()
else if (inApp.purchasePending) const CircularProgressIndicator()
else if (inApp.products.isNotEmpty)
ListTile(
leading: const Icon(Icons.attach_money),
title: Text(inApp.products[0].title + " - " + inApp.products[0].price),
subtitle: Text(inApp.products[0].description),
trailing: ElevatedButton( onPressed: () { ref.read(inAppPurchaseProvider.notifier).subscribe(product: inApp.products[0]);}, child: const Text('Subscribe'),
)
)
else const Text("Erreur produit"),
if (inApp.purchases.isNotEmpty)
ListView.builder(
itemCount:inApp.purchases.length,
itemBuilder: (context,index) {
return Text(inApp.purchases[index].status.toString()+" / Produit ID : "+inApp.purchases[index].productID+" / Date transaction : "+inApp.purchases[index].transactionDate!);
}
),
if (inApp.error.isNotEmpty) Text(inApp.error),
],
) : const Text("The Store is not Available")
)
)
);
}
}
subscription_provider.dart :
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
class InAppPurchaseClass {
InAppPurchaseClass({required this.isAvailable,required this.products,required this.purchases,required this.purchasePending,required this.loading, required this.error});
final bool isAvailable;
final List<ProductDetails> products;
final List<PurchaseDetails> purchases;
final bool purchasePending;
final bool loading;
final String error;
InAppPurchaseClass copyWith({bool? isAvailable,List<ProductDetails>? products,List<PurchaseDetails>? purchases,bool? purchasePending,bool? loading, String? error}) {
return InAppPurchaseClass(isAvailable:isAvailable??this.isAvailable,products:products??this.products,purchases:purchases??this.purchases,purchasePending:purchasePending??this.purchasePending,loading:loading??this.loading,error:error??this.error);
}
}
final inAppPurchaseProvider=StateNotifierProvider<InAppPurchaseNotifier,InAppPurchaseClass>((ref)=>InAppPurchaseNotifier(ref.read,InAppPurchase.instance));
class InAppPurchaseNotifier extends StateNotifier<InAppPurchaseClass> {
InAppPurchaseNotifier(this.read, this._inAppPurchase) : super(InAppPurchaseClass(isAvailable: false,products:[],purchases:[],purchasePending:false,loading:true, error: ""));
final Reader read;
final InAppPurchase _inAppPurchase;
late StreamSubscription<List<PurchaseDetails>>? _subscription;
init() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription= purchaseUpdated.listen((purchaseDetailsList) {
state=state.copyWith(purchases:purchaseDetailsList);
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
state=state.copyWith(purchasePending:true);
break;
case PurchaseStatus.purchased:
case PurchaseStatus.restored:
break; // Check if valid Purchase before break
case PurchaseStatus.error:
state=state.copyWith(error: purchaseDetails.error!.message);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
}
});
}, onDone: () {_subscription!.cancel();
}, onError: (error) {_subscription!.cancel();
});
updateStore(); // Load all to state product
}
updateStore() async {
state=state.copyWith(isAvailable: await _inAppPurchase.isAvailable());
ProductDetailsResponse response= await _inAppPurchase.queryProductDetails(<String>{"prenium_plan"}); // ID Of Product
state=state.copyWith(products:response.productDetails, loading: false);
}
subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(purchaseParam: purchaseParam);
}
@override
dispose(){
_subscription!.cancel();
super.dispose();
}
}