Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 0638189

Browse files
author
Chris Yang
authored
[in_app_purchase] Migrate to NNBD (#3555)
1 parent 2315531 commit 0638189

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1134
-709
lines changed

packages/in_app_purchase/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.4.0
2+
3+
* Migrate to nullsafety.
4+
* Deprecate `sandboxTesting`, introduce `simulatesAskToBuyInSandbox`.
5+
* **Breaking Change:**
6+
* Removed `callbackChannel` in `channels.dart`, see https://github.com/flutter/flutter/issues/69225.
7+
18
## 0.3.5+2
29

310
* Migrate deprecated references.

packages/in_app_purchase/build.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ targets:
55
options:
66
any_map: true
77
create_to_json: true
8-
nullable: false

packages/in_app_purchase/example/lib/main.dart

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,19 @@ class _MyApp extends StatefulWidget {
3232

3333
class _MyAppState extends State<_MyApp> {
3434
final InAppPurchaseConnection _connection = InAppPurchaseConnection.instance;
35-
StreamSubscription<List<PurchaseDetails>> _subscription;
35+
late StreamSubscription<List<PurchaseDetails>> _subscription;
3636
List<String> _notFoundIds = [];
3737
List<ProductDetails> _products = [];
3838
List<PurchaseDetails> _purchases = [];
3939
List<String> _consumables = [];
4040
bool _isAvailable = false;
4141
bool _purchasePending = false;
4242
bool _loading = true;
43-
String _queryProductError;
43+
String? _queryProductError;
4444

4545
@override
4646
void initState() {
47-
Stream purchaseUpdated =
47+
final Stream<List<PurchaseDetails>> purchaseUpdated =
4848
InAppPurchaseConnection.instance.purchaseUpdatedStream;
4949
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
5050
_listenToPurchaseUpdated(purchaseDetailsList);
@@ -76,7 +76,7 @@ class _MyAppState extends State<_MyApp> {
7676
await _connection.queryProductDetails(_kProductIds.toSet());
7777
if (productDetailResponse.error != null) {
7878
setState(() {
79-
_queryProductError = productDetailResponse.error.message;
79+
_queryProductError = productDetailResponse.error!.message;
8080
_isAvailable = isAvailable;
8181
_products = productDetailResponse.productDetails;
8282
_purchases = [];
@@ -146,7 +146,7 @@ class _MyAppState extends State<_MyApp> {
146146
);
147147
} else {
148148
stack.add(Center(
149-
child: Text(_queryProductError),
149+
child: Text(_queryProductError!),
150150
));
151151
}
152152
if (_purchasePending) {
@@ -235,7 +235,7 @@ class _MyAppState extends State<_MyApp> {
235235
}));
236236
productList.addAll(_products.map(
237237
(ProductDetails productDetails) {
238-
PurchaseDetails previousPurchase = purchases[productDetails.id];
238+
PurchaseDetails? previousPurchase = purchases[productDetails.id];
239239
return ListTile(
240240
title: Text(
241241
productDetails.title,
@@ -254,8 +254,7 @@ class _MyAppState extends State<_MyApp> {
254254
onPressed: () {
255255
PurchaseParam purchaseParam = PurchaseParam(
256256
productDetails: productDetails,
257-
applicationUserName: null,
258-
sandboxTesting: true);
257+
applicationUserName: null);
259258
if (productDetails.id == _kConsumableId) {
260259
_connection.buyConsumable(
261260
purchaseParam: purchaseParam,
@@ -329,7 +328,7 @@ class _MyAppState extends State<_MyApp> {
329328
void deliverProduct(PurchaseDetails purchaseDetails) async {
330329
// IMPORTANT!! Always verify a purchase purchase details before delivering the product.
331330
if (purchaseDetails.productID == _kConsumableId) {
332-
await ConsumableStore.save(purchaseDetails.purchaseID);
331+
await ConsumableStore.save(purchaseDetails.purchaseID!);
333332
List<String> consumables = await ConsumableStore.load();
334333
setState(() {
335334
_purchasePending = false;
@@ -365,7 +364,7 @@ class _MyAppState extends State<_MyApp> {
365364
showPendingUI();
366365
} else {
367366
if (purchaseDetails.status == PurchaseStatus.error) {
368-
handleError(purchaseDetails.error);
367+
handleError(purchaseDetails.error!);
369368
} else if (purchaseDetails.status == PurchaseStatus.purchased) {
370369
bool valid = await _verifyPurchase(purchaseDetails);
371370
if (valid) {

packages/in_app_purchase/example/pubspec.yaml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ author: Flutter Team <[email protected]>
55
dependencies:
66
flutter:
77
sdk: flutter
8-
cupertino_icons: ^0.1.2
9-
shared_preferences: ^0.5.2
8+
shared_preferences: ^2.0.0-nullsafety.1
109

1110
dev_dependencies:
12-
test: ^1.5.2
1311
flutter_driver:
1412
sdk: flutter
1513
in_app_purchase:
@@ -21,11 +19,11 @@ dev_dependencies:
2119
path: ../
2220
integration_test:
2321
path: ../../integration_test
24-
pedantic: ^1.8.0
22+
pedantic: ^1.10.0
2523

2624
flutter:
2725
uses-material-design: true
2826

2927
environment:
30-
sdk: ">=2.3.0 <3.0.0"
28+
sdk: ">=2.12.0-259.9.beta <3.0.0"
3129
flutter: ">=1.9.1+hotfix.2"

packages/in_app_purchase/example/test_driver/test/integration_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
// @dart = 2.9
56
import 'dart:async';
67
import 'dart:convert';
78
import 'dart:io';

packages/in_app_purchase/integration_test/in_app_purchase_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
// @dart = 2.9
56
import 'package:flutter_test/flutter_test.dart';
67
import 'package:in_app_purchase/in_app_purchase.dart';
78
import 'package:integration_test/integration_test.dart';

packages/in_app_purchase/ios/Classes/FIAPReceiptManager.m

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
//
6-
// FIAPReceiptManager.m
7-
// in_app_purchase
8-
//
9-
// Created by Chris Yang on 3/2/19.
10-
//
11-
125
#import "FIAPReceiptManager.h"
136
#import <Flutter/Flutter.h>
147

packages/in_app_purchase/ios/Classes/InAppPurchasePlugin.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar
7575
}];
7676
[_paymentQueueHandler startObservingPaymentQueue];
7777
_callbackChannel =
78-
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase_callback"
78+
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase"
7979
binaryMessenger:[registrar messenger]];
8080
return self;
8181
}
@@ -290,7 +290,7 @@ - (void)refreshReceipt:(FlutterMethodCall *)call result:(FlutterResult)result {
290290
}];
291291
}
292292

293-
#pragma mark - delegates
293+
#pragma mark - delegates:
294294

295295
- (void)handleTransactionsUpdated:(NSArray<SKPaymentTransaction *> *)transactions {
296296
NSMutableArray *maps = [NSMutableArray new];

packages/in_app_purchase/lib/src/billing_client_wrappers/billing_client_wrapper.dart

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,7 @@ class BillingClient {
5353
bool _enablePendingPurchases = false;
5454

5555
/// Creates a billing client.
56-
///
57-
/// The `onPurchasesUpdated` parameter must not be null.
5856
BillingClient(PurchasesUpdatedListener onPurchasesUpdated) {
59-
assert(onPurchasesUpdated != null);
6057
channel.setMethodCallHandler(callHandler);
6158
_callbacks[kOnPurchasesUpdated] = [onPurchasesUpdated];
6259
}
@@ -74,8 +71,11 @@ class BillingClient {
7471
/// Calls
7572
/// [`BillingClient#isReady()`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#isReady())
7673
/// to get the ready status of the BillingClient instance.
77-
Future<bool> isReady() async =>
78-
await channel.invokeMethod<bool>('BillingClient#isReady()');
74+
Future<bool> isReady() async {
75+
final bool? ready =
76+
await channel.invokeMethod<bool>('BillingClient#isReady()');
77+
return ready ?? false;
78+
}
7979

8080
/// Enable the [BillingClientWrapper] to handle pending purchases.
8181
///
@@ -100,20 +100,21 @@ class BillingClient {
100100
/// This triggers the creation of a new `BillingClient` instance in Java if
101101
/// one doesn't already exist.
102102
Future<BillingResultWrapper> startConnection(
103-
{@required
104-
OnBillingServiceDisconnected onBillingServiceDisconnected}) async {
103+
{required OnBillingServiceDisconnected
104+
onBillingServiceDisconnected}) async {
105105
assert(_enablePendingPurchases,
106106
'enablePendingPurchases() must be called before calling startConnection');
107107
List<Function> disconnectCallbacks =
108108
_callbacks[_kOnBillingServiceDisconnected] ??= [];
109109
disconnectCallbacks.add(onBillingServiceDisconnected);
110-
return BillingResultWrapper.fromJson(await channel
111-
.invokeMapMethod<String, dynamic>(
112-
"BillingClient#startConnection(BillingClientStateListener)",
113-
<String, dynamic>{
114-
'handle': disconnectCallbacks.length - 1,
115-
'enablePendingPurchases': _enablePendingPurchases
116-
}));
110+
return BillingResultWrapper.fromJson((await channel
111+
.invokeMapMethod<String, dynamic>(
112+
"BillingClient#startConnection(BillingClientStateListener)",
113+
<String, dynamic>{
114+
'handle': disconnectCallbacks.length - 1,
115+
'enablePendingPurchases': _enablePendingPurchases
116+
})) ??
117+
<String, dynamic>{});
117118
}
118119

119120
/// Calls
@@ -137,15 +138,16 @@ class BillingClient {
137138
/// `SkuDetailsParams` as direct arguments instead of requiring it constructed
138139
/// and passed in as a class.
139140
Future<SkuDetailsResponseWrapper> querySkuDetails(
140-
{@required SkuType skuType, @required List<String> skusList}) async {
141+
{required SkuType skuType, required List<String> skusList}) async {
141142
final Map<String, dynamic> arguments = <String, dynamic>{
142143
'skuType': SkuTypeConverter().toJson(skuType),
143144
'skusList': skusList
144145
};
145-
return SkuDetailsResponseWrapper.fromJson(await channel.invokeMapMethod<
146-
String, dynamic>(
147-
'BillingClient#querySkuDetailsAsync(SkuDetailsParams, SkuDetailsResponseListener)',
148-
arguments));
146+
return SkuDetailsResponseWrapper.fromJson((await channel.invokeMapMethod<
147+
String, dynamic>(
148+
'BillingClient#querySkuDetailsAsync(SkuDetailsParams, SkuDetailsResponseListener)',
149+
arguments)) ??
150+
<String, dynamic>{});
149151
}
150152

151153
/// Attempt to launch the Play Billing Flow for a given [skuDetails].
@@ -172,16 +174,17 @@ class BillingClient {
172174
/// and [the given
173175
/// accountId](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder.html#setAccountId(java.lang.String)).
174176
Future<BillingResultWrapper> launchBillingFlow(
175-
{@required String sku, String accountId}) async {
177+
{required String sku, String? accountId}) async {
176178
assert(sku != null);
177179
final Map<String, dynamic> arguments = <String, dynamic>{
178180
'sku': sku,
179181
'accountId': accountId,
180182
};
181183
return BillingResultWrapper.fromJson(
182-
await channel.invokeMapMethod<String, dynamic>(
183-
'BillingClient#launchBillingFlow(Activity, BillingFlowParams)',
184-
arguments));
184+
(await channel.invokeMapMethod<String, dynamic>(
185+
'BillingClient#launchBillingFlow(Activity, BillingFlowParams)',
186+
arguments)) ??
187+
<String, dynamic>{});
185188
}
186189

187190
/// Fetches recent purchases for the given [SkuType].
@@ -197,10 +200,12 @@ class BillingClient {
197200
/// skutype)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#querypurchases).
198201
Future<PurchasesResultWrapper> queryPurchases(SkuType skuType) async {
199202
assert(skuType != null);
200-
return PurchasesResultWrapper.fromJson(await channel
201-
.invokeMapMethod<String, dynamic>(
202-
'BillingClient#queryPurchases(String)',
203-
<String, dynamic>{'skuType': SkuTypeConverter().toJson(skuType)}));
203+
return PurchasesResultWrapper.fromJson((await channel
204+
.invokeMapMethod<String, dynamic>(
205+
'BillingClient#queryPurchases(String)', <String, dynamic>{
206+
'skuType': SkuTypeConverter().toJson(skuType)
207+
})) ??
208+
<String, dynamic>{});
204209
}
205210

206211
/// Fetches purchase history for the given [SkuType].
@@ -218,31 +223,34 @@ class BillingClient {
218223
/// listener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#querypurchasehistoryasync).
219224
Future<PurchasesHistoryResult> queryPurchaseHistory(SkuType skuType) async {
220225
assert(skuType != null);
221-
return PurchasesHistoryResult.fromJson(await channel.invokeMapMethod<String,
222-
dynamic>(
223-
'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)',
224-
<String, dynamic>{'skuType': SkuTypeConverter().toJson(skuType)}));
226+
return PurchasesHistoryResult.fromJson((await channel.invokeMapMethod<
227+
String, dynamic>(
228+
'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)',
229+
<String, dynamic>{
230+
'skuType': SkuTypeConverter().toJson(skuType)
231+
})) ??
232+
<String, dynamic>{});
225233
}
226234

227235
/// Consumes a given in-app product.
228236
///
229237
/// Consuming can only be done on an item that's owned, and as a result of consumption, the user will no longer own it.
230238
/// Consumption is done asynchronously. The method returns a Future containing a [BillingResultWrapper].
231239
///
232-
/// The `purchaseToken` must not be null.
233240
/// The `developerPayload` is the developer data associated with the purchase to be consumed, it defaults to null.
234241
///
235242
/// This wraps [`BillingClient#consumeAsync(String, ConsumeResponseListener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#consumeAsync(java.lang.String,%20com.android.billingclient.api.ConsumeResponseListener))
236243
Future<BillingResultWrapper> consumeAsync(String purchaseToken,
237-
{String developerPayload}) async {
244+
{String? developerPayload}) async {
238245
assert(purchaseToken != null);
239-
return BillingResultWrapper.fromJson(await channel
240-
.invokeMapMethod<String, dynamic>(
241-
'BillingClient#consumeAsync(String, ConsumeResponseListener)',
242-
<String, String>{
243-
'purchaseToken': purchaseToken,
244-
'developerPayload': developerPayload,
245-
}));
246+
return BillingResultWrapper.fromJson((await channel
247+
.invokeMapMethod<String, dynamic>(
248+
'BillingClient#consumeAsync(String, ConsumeResponseListener)',
249+
<String, dynamic>{
250+
'purchaseToken': purchaseToken,
251+
'developerPayload': developerPayload,
252+
})) ??
253+
<String, dynamic>{});
246254
}
247255

248256
/// Acknowledge an in-app purchase.
@@ -261,20 +269,20 @@ class BillingClient {
261269
/// Please refer to [acknowledge](https://developer.android.com/google/play/billing/billing_library_overview#acknowledge) for more
262270
/// details.
263271
///
264-
/// The `purchaseToken` must not be null.
265272
/// The `developerPayload` is the developer data associated with the purchase to be consumed, it defaults to null.
266273
///
267274
/// This wraps [`BillingClient#acknowledgePurchase(String, AcknowledgePurchaseResponseListener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#acknowledgePurchase(com.android.billingclient.api.AcknowledgePurchaseParams,%20com.android.billingclient.api.AcknowledgePurchaseResponseListener))
268275
Future<BillingResultWrapper> acknowledgePurchase(String purchaseToken,
269-
{String developerPayload}) async {
276+
{String? developerPayload}) async {
270277
assert(purchaseToken != null);
271-
return BillingResultWrapper.fromJson(await channel.invokeMapMethod<String,
272-
dynamic>(
273-
'BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)',
274-
<String, String>{
275-
'purchaseToken': purchaseToken,
276-
'developerPayload': developerPayload,
277-
}));
278+
return BillingResultWrapper.fromJson((await channel.invokeMapMethod<String,
279+
dynamic>(
280+
'BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)',
281+
<String, dynamic>{
282+
'purchaseToken': purchaseToken,
283+
'developerPayload': developerPayload,
284+
})) ??
285+
<String, dynamic>{});
278286
}
279287

280288
/// The method call handler for [channel].
@@ -283,15 +291,15 @@ class BillingClient {
283291
switch (call.method) {
284292
case kOnPurchasesUpdated:
285293
// The purchases updated listener is a singleton.
286-
assert(_callbacks[kOnPurchasesUpdated].length == 1);
294+
assert(_callbacks[kOnPurchasesUpdated]!.length == 1);
287295
final PurchasesUpdatedListener listener =
288-
_callbacks[kOnPurchasesUpdated].first;
296+
_callbacks[kOnPurchasesUpdated]!.first as PurchasesUpdatedListener;
289297
listener(PurchasesResultWrapper.fromJson(
290298
call.arguments.cast<String, dynamic>()));
291299
break;
292300
case _kOnBillingServiceDisconnected:
293301
final int handle = call.arguments['handle'];
294-
await _callbacks[_kOnBillingServiceDisconnected][handle]();
302+
await _callbacks[_kOnBillingServiceDisconnected]![handle]();
295303
break;
296304
}
297305
}

0 commit comments

Comments
 (0)