[in_app_purchase] Update app-facing package to new analysis options (#4969)
diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md
index 588993a..134fd90 100644
--- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md
+++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 3.0.1
+
+* Internal code cleanup for stricter analysis options.
+
## 3.0.0
* **BREAKING CHANGE** Updates `restorePurchases` to emit an empty list of purchases on StoreKit when there are no purchases to restore (same as Android).
diff --git a/packages/in_app_purchase/in_app_purchase/analysis_options.yaml b/packages/in_app_purchase/in_app_purchase/analysis_options.yaml
deleted file mode 100644
index 5aeb4e7..0000000
--- a/packages/in_app_purchase/in_app_purchase/analysis_options.yaml
+++ /dev/null
@@ -1 +0,0 @@
-include: ../../../analysis_options_legacy.yaml
diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart b/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart
index 4d10a50..448efcf 100644
--- a/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart
+++ b/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart
@@ -5,13 +5,14 @@
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
+// ignore: avoid_classes_with_only_static_members
/// A store of consumable items.
///
/// This is a development prototype tha stores consumables in the shared
/// preferences. Do not use this in real world apps.
class ConsumableStore {
static const String _kPrefKey = 'consumables';
- static Future<void> _writes = Future.value();
+ static Future<void> _writes = Future<void>.value();
/// Adds a consumable with ID `id` to the store.
///
@@ -32,19 +33,19 @@
/// Returns the list of consumables from the store.
static Future<List<String>> load() async {
return (await SharedPreferences.getInstance()).getStringList(_kPrefKey) ??
- [];
+ <String>[];
}
static Future<void> _doSave(String id) async {
- List<String> cached = await load();
- SharedPreferences prefs = await SharedPreferences.getInstance();
+ final List<String> cached = await load();
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
cached.add(id);
await prefs.setStringList(_kPrefKey, cached);
}
static Future<void> _doConsume(String id) async {
- List<String> cached = await load();
- SharedPreferences prefs = await SharedPreferences.getInstance();
+ final List<String> cached = await load();
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
cached.remove(id);
await prefs.setStringList(_kPrefKey, cached);
}
diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart
index 5d2f04c..651652b 100644
--- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart
+++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart
@@ -39,10 +39,10 @@
class _MyAppState extends State<_MyApp> {
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
late StreamSubscription<List<PurchaseDetails>> _subscription;
- List<String> _notFoundIds = [];
- List<ProductDetails> _products = [];
- List<PurchaseDetails> _purchases = [];
- List<String> _consumables = [];
+ List<String> _notFoundIds = <String>[];
+ List<ProductDetails> _products = <ProductDetails>[];
+ List<PurchaseDetails> _purchases = <PurchaseDetails>[];
+ List<String> _consumables = <String>[];
bool _isAvailable = false;
bool _purchasePending = false;
bool _loading = true;
@@ -52,11 +52,12 @@
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated =
_inAppPurchase.purchaseStream;
- _subscription = purchaseUpdated.listen((purchaseDetailsList) {
+ _subscription =
+ purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
}, onDone: () {
_subscription.cancel();
- }, onError: (error) {
+ }, onError: (Object error) {
// handle error here.
});
initStoreInfo();
@@ -68,10 +69,10 @@
if (!isAvailable) {
setState(() {
_isAvailable = isAvailable;
- _products = [];
- _purchases = [];
- _notFoundIds = [];
- _consumables = [];
+ _products = <ProductDetails>[];
+ _purchases = <PurchaseDetails>[];
+ _notFoundIds = <String>[];
+ _consumables = <String>[];
_purchasePending = false;
_loading = false;
});
@@ -79,21 +80,22 @@
}
if (Platform.isIOS) {
- var iosPlatformAddition = _inAppPurchase
- .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
+ final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
+ _inAppPurchase
+ .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
}
- ProductDetailsResponse productDetailResponse =
+ final ProductDetailsResponse productDetailResponse =
await _inAppPurchase.queryProductDetails(_kProductIds.toSet());
if (productDetailResponse.error != null) {
setState(() {
_queryProductError = productDetailResponse.error!.message;
_isAvailable = isAvailable;
_products = productDetailResponse.productDetails;
- _purchases = [];
+ _purchases = <PurchaseDetails>[];
_notFoundIds = productDetailResponse.notFoundIDs;
- _consumables = [];
+ _consumables = <String>[];
_purchasePending = false;
_loading = false;
});
@@ -105,16 +107,16 @@
_queryProductError = null;
_isAvailable = isAvailable;
_products = productDetailResponse.productDetails;
- _purchases = [];
+ _purchases = <PurchaseDetails>[];
_notFoundIds = productDetailResponse.notFoundIDs;
- _consumables = [];
+ _consumables = <String>[];
_purchasePending = false;
_loading = false;
});
return;
}
- List<String> consumables = await ConsumableStore.load();
+ final List<String> consumables = await ConsumableStore.load();
setState(() {
_isAvailable = isAvailable;
_products = productDetailResponse.productDetails;
@@ -128,8 +130,9 @@
@override
void dispose() {
if (Platform.isIOS) {
- var iosPlatformAddition = _inAppPurchase
- .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
+ final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
+ _inAppPurchase
+ .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
iosPlatformAddition.setDelegate(null);
}
_subscription.cancel();
@@ -138,11 +141,11 @@
@override
Widget build(BuildContext context) {
- List<Widget> stack = [];
+ final List<Widget> stack = <Widget>[];
if (_queryProductError == null) {
stack.add(
ListView(
- children: [
+ children: <Widget>[
_buildConnectionCheckTile(),
_buildProductList(),
_buildConsumableBox(),
@@ -158,10 +161,10 @@
if (_purchasePending) {
stack.add(
Stack(
- children: [
+ children: const <Widget>[
Opacity(
opacity: 0.3,
- child: const ModalBarrier(dismissible: false, color: Colors.grey),
+ child: ModalBarrier(dismissible: false, color: Colors.grey),
),
Center(
child: CircularProgressIndicator(),
@@ -185,7 +188,7 @@
Card _buildConnectionCheckTile() {
if (_loading) {
- return Card(child: ListTile(title: const Text('Trying to connect...')));
+ return const Card(child: ListTile(title: Text('Trying to connect...')));
}
final Widget storeHeader = ListTile(
leading: Icon(_isAvailable ? Icons.check : Icons.block,
@@ -196,8 +199,8 @@
final List<Widget> children = <Widget>[storeHeader];
if (!_isAvailable) {
- children.addAll([
- Divider(),
+ children.addAll(<Widget>[
+ const Divider(),
ListTile(
title: Text('Not connected',
style: TextStyle(color: ThemeData.light().errorColor)),
@@ -211,29 +214,30 @@
Card _buildProductList() {
if (_loading) {
- return Card(
- child: (ListTile(
+ return const Card(
+ child: ListTile(
leading: CircularProgressIndicator(),
- title: Text('Fetching products...'))));
+ title: Text('Fetching products...')));
}
if (!_isAvailable) {
- return Card();
+ return const Card();
}
- final ListTile productHeader = ListTile(title: Text('Products for Sale'));
- List<ListTile> productList = <ListTile>[];
+ const ListTile productHeader = ListTile(title: Text('Products for Sale'));
+ final List<ListTile> productList = <ListTile>[];
if (_notFoundIds.isNotEmpty) {
productList.add(ListTile(
title: Text('[${_notFoundIds.join(", ")}] not found',
style: TextStyle(color: ThemeData.light().errorColor)),
- subtitle: Text(
+ subtitle: const Text(
'This app needs special configuration to run. Please see example/README.md for instructions.')));
}
// This loading previous purchases code is just a demo. Please do not use this as it is.
// In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it.
// We recommend that you use your own server to verify the purchase data.
- Map<String, PurchaseDetails> purchases =
- Map.fromEntries(_purchases.map((PurchaseDetails purchase) {
+ final Map<String, PurchaseDetails> purchases =
+ Map<String, PurchaseDetails>.fromEntries(
+ _purchases.map((PurchaseDetails purchase) {
if (purchase.pendingCompletePurchase) {
_inAppPurchase.completePurchase(purchase);
}
@@ -241,7 +245,7 @@
}));
productList.addAll(_products.map(
(ProductDetails productDetails) {
- PurchaseDetails? previousPurchase = purchases[productDetails.id];
+ final PurchaseDetails? previousPurchase = purchases[productDetails.id];
return ListTile(
title: Text(
productDetails.title,
@@ -252,7 +256,7 @@
trailing: previousPurchase != null
? IconButton(
onPressed: () => confirmPriceChange(context),
- icon: Icon(Icons.upgrade))
+ icon: const Icon(Icons.upgrade))
: TextButton(
child: Text(productDetails.price),
style: TextButton.styleFrom(
@@ -267,7 +271,7 @@
// verify the latest status of you your subscription by using server side receipt validation
// and update the UI accordingly. The subscription purchase status shown
// inside the app may not be accurate.
- final oldSubscription =
+ final GooglePlayPurchaseDetails? oldSubscription =
_getOldSubscription(productDetails, purchases);
purchaseParam = GooglePlayPurchaseParam(
@@ -301,26 +305,26 @@
));
return Card(
- child:
- Column(children: <Widget>[productHeader, Divider()] + productList));
+ child: Column(
+ children: <Widget>[productHeader, const Divider()] + productList));
}
Card _buildConsumableBox() {
if (_loading) {
- return Card(
- child: (ListTile(
+ return const Card(
+ child: ListTile(
leading: CircularProgressIndicator(),
- title: Text('Fetching consumables...'))));
+ title: Text('Fetching consumables...')));
}
if (!_isAvailable || _notFoundIds.contains(_kConsumableId)) {
- return Card();
+ return const Card();
}
- final ListTile consumableHeader =
+ const ListTile consumableHeader =
ListTile(title: Text('Purchased consumables'));
final List<Widget> tokens = _consumables.map((String id) {
return GridTile(
child: IconButton(
- icon: Icon(
+ icon: const Icon(
Icons.stars,
size: 42.0,
color: Colors.orange,
@@ -333,12 +337,12 @@
return Card(
child: Column(children: <Widget>[
consumableHeader,
- Divider(),
+ const Divider(),
GridView.count(
crossAxisCount: 5,
children: tokens,
shrinkWrap: true,
- padding: EdgeInsets.all(16.0),
+ padding: const EdgeInsets.all(16.0),
)
]));
}
@@ -353,9 +357,9 @@
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
- children: [
+ children: <Widget>[
TextButton(
- child: Text('Restore purchases'),
+ child: const Text('Restore purchases'),
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
primary: Colors.white,
@@ -381,11 +385,11 @@
});
}
- void deliverProduct(PurchaseDetails purchaseDetails) async {
+ Future<void> deliverProduct(PurchaseDetails purchaseDetails) async {
// IMPORTANT!! Always verify purchase details before delivering the product.
if (purchaseDetails.productID == _kConsumableId) {
await ConsumableStore.save(purchaseDetails.purchaseID!);
- List<String> consumables = await ConsumableStore.load();
+ final List<String> consumables = await ConsumableStore.load();
setState(() {
_purchasePending = false;
_consumables = consumables;
@@ -414,8 +418,9 @@
// handle invalid purchase here if _verifyPurchase` failed.
}
- void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
- purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
+ Future<void> _listenToPurchaseUpdated(
+ List<PurchaseDetails> purchaseDetailsList) async {
+ for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
if (purchaseDetails.status == PurchaseStatus.pending) {
showPendingUI();
} else {
@@ -423,7 +428,7 @@
handleError(purchaseDetails.error!);
} else if (purchaseDetails.status == PurchaseStatus.purchased ||
purchaseDetails.status == PurchaseStatus.restored) {
- bool valid = await _verifyPurchase(purchaseDetails);
+ final bool valid = await _verifyPurchase(purchaseDetails);
if (valid) {
deliverProduct(purchaseDetails);
} else {
@@ -443,7 +448,7 @@
await _inAppPurchase.completePurchase(purchaseDetails);
}
}
- });
+ }
}
Future<void> confirmPriceChange(BuildContext context) async {
@@ -451,26 +456,27 @@
final InAppPurchaseAndroidPlatformAddition androidAddition =
_inAppPurchase
.getPlatformAddition<InAppPurchaseAndroidPlatformAddition>();
- var priceChangeConfirmationResult =
+ final BillingResultWrapper priceChangeConfirmationResult =
await androidAddition.launchPriceChangeConfirmationFlow(
sku: 'purchaseId',
);
if (priceChangeConfirmationResult.responseCode == BillingResponse.ok) {
- ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+ ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Price change accepted'),
));
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
priceChangeConfirmationResult.debugMessage ??
- "Price change failed with code ${priceChangeConfirmationResult.responseCode}",
+ 'Price change failed with code ${priceChangeConfirmationResult.responseCode}',
),
));
}
}
if (Platform.isIOS) {
- var iapStoreKitPlatformAddition = _inAppPurchase
- .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
+ final InAppPurchaseStoreKitPlatformAddition iapStoreKitPlatformAddition =
+ _inAppPurchase
+ .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
await iapStoreKitPlatformAddition.showPriceConsentIfNeeded();
}
}
@@ -488,11 +494,11 @@
if (productDetails.id == _kSilverSubscriptionId &&
purchases[_kGoldSubscriptionId] != null) {
oldSubscription =
- purchases[_kGoldSubscriptionId] as GooglePlayPurchaseDetails;
+ purchases[_kGoldSubscriptionId]! as GooglePlayPurchaseDetails;
} else if (productDetails.id == _kGoldSubscriptionId &&
purchases[_kSilverSubscriptionId] != null) {
oldSubscription =
- purchases[_kSilverSubscriptionId] as GooglePlayPurchaseDetails;
+ purchases[_kSilverSubscriptionId]! as GooglePlayPurchaseDetails;
}
return oldSubscription;
}
diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
index a75aaa6..4a79b19 100644
--- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml
@@ -9,8 +9,6 @@
dependencies:
flutter:
sdk: flutter
- shared_preferences: ^2.0.0
-
in_app_purchase:
# When depending on this package from a real application you should use:
# in_app_purchase: ^x.y.z
@@ -18,6 +16,7 @@
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
+ shared_preferences: ^2.0.0
dev_dependencies:
flutter_driver:
@@ -25,7 +24,6 @@
integration_test:
sdk: flutter
- pedantic: ^1.10.0
flutter:
uses-material-design: true
diff --git a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart
index 2a14f2b..df05d8c 100644
--- a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart
+++ b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart
@@ -4,8 +4,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
-import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
+import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
export 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'
diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml
index 80c150d..139d583 100644
--- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play.
repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
-version: 3.0.0
+version: 3.0.1
environment:
sdk: ">=2.12.0 <3.0.0"
@@ -19,8 +19,8 @@
dependencies:
flutter:
sdk: flutter
- in_app_purchase_platform_interface: ^1.0.0
in_app_purchase_android: ^0.2.1
+ in_app_purchase_platform_interface: ^1.0.0
in_app_purchase_storekit: ^0.3.0+1
dev_dependencies:
@@ -30,6 +30,5 @@
sdk: flutter
integration_test:
sdk: flutter
- pedantic: ^1.10.0
plugin_platform_interface: ^2.0.0
test: ^1.16.0
diff --git a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart
index b8c7bd8..644d26e 100644
--- a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart
+++ b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart
@@ -65,7 +65,7 @@
test('queryProductDetails', () async {
final ProductDetailsResponse response =
- await inAppPurchase.queryProductDetails(Set<String>());
+ await inAppPurchase.queryProductDetails(<String>{});
expect(response.notFoundIDs.isEmpty, true);
expect(response.productDetails.isEmpty, true);
expect(fakePlatform.log, <Matcher>[
@@ -87,22 +87,24 @@
});
test('buyConsumable', () async {
- final purchaseParam = PurchaseParam(productDetails: productDetails);
+ final PurchaseParam purchaseParam =
+ PurchaseParam(productDetails: productDetails);
final bool result = await inAppPurchase.buyConsumable(
purchaseParam: purchaseParam,
);
expect(result, true);
expect(fakePlatform.log, <Matcher>[
- isMethodCall('buyConsumable', arguments: {
- "purchaseParam": purchaseParam,
- "autoConsume": true,
+ isMethodCall('buyConsumable', arguments: <dynamic, dynamic>{
+ 'purchaseParam': purchaseParam,
+ 'autoConsume': true,
}),
]);
});
test('buyConsumable with autoConsume=false', () async {
- final purchaseParam = PurchaseParam(productDetails: productDetails);
+ final PurchaseParam purchaseParam =
+ PurchaseParam(productDetails: productDetails);
final bool result = await inAppPurchase.buyConsumable(
purchaseParam: purchaseParam,
autoConsume: false,
@@ -110,9 +112,9 @@
expect(result, true);
expect(fakePlatform.log, <Matcher>[
- isMethodCall('buyConsumable', arguments: {
- "purchaseParam": purchaseParam,
- "autoConsume": false,
+ isMethodCall('buyConsumable', arguments: <dynamic, dynamic>{
+ 'purchaseParam': purchaseParam,
+ 'autoConsume': false,
}),
]);
});
@@ -138,31 +140,33 @@
class MockInAppPurchasePlatform extends Fake
with MockPlatformInterfaceMixin
implements InAppPurchasePlatform {
- final List<MethodCall> log = [];
+ final List<MethodCall> log = <MethodCall>[];
@override
Future<bool> isAvailable() {
- log.add(MethodCall('isAvailable'));
- return Future.value(true);
+ log.add(const MethodCall('isAvailable'));
+ return Future<bool>.value(true);
}
@override
Stream<List<PurchaseDetails>> get purchaseStream {
- log.add(MethodCall('purchaseStream'));
- return Stream.empty();
+ log.add(const MethodCall('purchaseStream'));
+ return const Stream<List<PurchaseDetails>>.empty();
}
@override
Future<ProductDetailsResponse> queryProductDetails(Set<String> identifiers) {
- log.add(MethodCall('queryProductDetails'));
- return Future.value(
- ProductDetailsResponse(productDetails: [], notFoundIDs: []));
+ log.add(const MethodCall('queryProductDetails'));
+ return Future<ProductDetailsResponse>.value(ProductDetailsResponse(
+ productDetails: <ProductDetails>[],
+ notFoundIDs: <String>[],
+ ));
}
@override
Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) {
- log.add(MethodCall('buyNonConsumable'));
- return Future.value(true);
+ log.add(const MethodCall('buyNonConsumable'));
+ return Future<bool>.value(true);
}
@override
@@ -170,22 +174,22 @@
required PurchaseParam purchaseParam,
bool autoConsume = true,
}) {
- log.add(MethodCall('buyConsumable', {
- "purchaseParam": purchaseParam,
- "autoConsume": autoConsume,
+ log.add(MethodCall('buyConsumable', <String, Object?>{
+ 'purchaseParam': purchaseParam,
+ 'autoConsume': autoConsume,
}));
- return Future.value(true);
+ return Future<bool>.value(true);
}
@override
Future<void> completePurchase(PurchaseDetails purchase) {
- log.add(MethodCall('completePurchase'));
- return Future.value(null);
+ log.add(const MethodCall('completePurchase'));
+ return Future<void>.value(null);
}
@override
Future<void> restorePurchases({String? applicationUserName}) {
- log.add(MethodCall('restorePurchases'));
- return Future.value(null);
+ log.add(const MethodCall('restorePurchases'));
+ return Future<void>.value(null);
}
}
diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml
index e08024c..23b7f7b 100644
--- a/script/configs/custom_analysis.yaml
+++ b/script/configs/custom_analysis.yaml
@@ -13,4 +13,3 @@
# https://github.com/flutter/flutter/issues/76229
- google_maps_flutter/google_maps_flutter_platform_interface
- google_maps_flutter/google_maps_flutter_web
-- in_app_purchase/in_app_purchase