IAP: Finish null safety migration

I'm quite uncomfortable with this code. It seems to work, but the error
handling is really bad.
This commit is contained in:
Vishesh Handa
2021-06-02 11:19:28 +02:00
parent 4cdbf30f44
commit a0562dfe94
4 changed files with 55 additions and 56 deletions

View File

@ -420,7 +420,7 @@ class _ChecklistItemTileState extends State<ChecklistItemTile> {
}
class AddItemButton extends StatelessWidget {
final Function onPressed;
final void Function() onPressed;
AddItemButton({Key? key, required this.onPressed}) : super(key: key);
@ -440,7 +440,7 @@ class AddItemButton extends StatelessWidget {
child: IconButton(
padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
icon: const Icon(Icons.add),
onPressed: onPressed as void Function()?,
onPressed: onPressed,
),
),
],
@ -450,7 +450,7 @@ class AddItemButton extends StatelessWidget {
);
return GestureDetector(
onTap: onPressed as void Function()?,
onTap: onPressed,
child: tile,
);
}

View File

@ -1,5 +1,3 @@
// @dart=2.9
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
@ -13,17 +11,17 @@ import 'package:gitjournal/widgets/purchase_slider.dart';
// ignore_for_file: cancel_subscriptions
typedef PurchaseCallback = void Function(String, SubscriptionStatus);
typedef PurchaseCallback = void Function(String, SubscriptionStatus?);
class PurchaseManager {
InAppPurchaseConnection con;
StreamSubscription<List<PurchaseDetails>> _subscription;
late InAppPurchaseConnection con;
late StreamSubscription<List<PurchaseDetails>> _subscription;
List<PurchaseCallback> _callbacks = [];
static String error;
static PurchaseManager _instance;
static String? error;
static PurchaseManager? _instance;
static Future<PurchaseManager> init() async {
static Future<PurchaseManager?> init() async {
if (_instance != null) {
return _instance;
}
@ -31,9 +29,9 @@ class PurchaseManager {
_instance = PurchaseManager();
InAppPurchaseConnection.enablePendingPurchases();
_instance.con = InAppPurchaseConnection.instance;
_instance!.con = InAppPurchaseConnection.instance;
final bool available = await _instance.con.isAvailable();
final bool available = await _instance!.con.isAvailable();
if (!available) {
error = "Store cannot be reached";
_instance = null;
@ -41,7 +39,7 @@ class PurchaseManager {
}
// Start listening for changes
var i = _instance;
var i = _instance!;
final purchaseUpdates = i.con.purchaseUpdatedStream;
i._subscription = purchaseUpdates.listen(i._listenToPurchaseUpdated);
@ -49,7 +47,7 @@ class PurchaseManager {
}
void destroy() {
_instance._subscription.cancel();
_instance!._subscription.cancel();
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetails) async {
@ -68,7 +66,7 @@ class PurchaseManager {
}
if (purchaseDetails.status == PurchaseStatus.error) {
_handleIAPError(purchaseDetails.error);
_handleIAPError(purchaseDetails.error!);
return;
} else if (purchaseDetails.status == PurchaseStatus.purchased) {
Log.i("Verifying purchase sub");
@ -127,10 +125,11 @@ class PurchaseManager {
/// Returns the ProductDetails sorted by price
Future<ProductDetailsResponse> queryProductDetails(Set<String> skus) async {
// Cache this response?
var response = await _instance.con.queryProductDetails(skus);
// FIXME: What if the sotre cannot be reached?
var response = await _instance!.con.queryProductDetails(skus);
response.productDetails.sort((a, b) {
var pa = PaymentInfo.fromProductDetail(a);
var pb = PaymentInfo.fromProductDetail(b);
var pa = PaymentInfo.fromProductDetail(a)!;
var pb = PaymentInfo.fromProductDetail(b)!;
return pa.value.compareTo(pb.value);
});

View File

@ -32,7 +32,7 @@ class ListPreference extends StatelessWidget {
label: o,
value: o,
groupValue: currentOption,
onChanged: (String val) {
onChanged: (String? val) {
Navigator.of(context).pop(val);
},
);
@ -75,7 +75,7 @@ class _LabeledRadio extends StatelessWidget {
final String label;
final String? groupValue;
final String? value;
final Function onChanged;
final void Function(String?) onChanged;
@override
Widget build(BuildContext context) {
@ -88,7 +88,7 @@ class _LabeledRadio extends StatelessWidget {
Radio<String?>(
groupValue: groupValue,
value: value,
onChanged: onChanged as void Function(String?)?,
onChanged: onChanged,
),
Text(label),
],

View File

@ -1,5 +1,3 @@
// @dart=2.9
import 'dart:async';
import 'package:flutter/material.dart';
@ -17,7 +15,7 @@ import 'package:gitjournal/utils/logger.dart';
import 'package:gitjournal/widgets/purchase_slider.dart';
class PurchaseButton extends StatelessWidget {
final ProductDetails product;
final ProductDetails? product;
final String timePeriod;
final bool subscription;
final Func1<bool, void> purchaseStarted;
@ -26,9 +24,9 @@ class PurchaseButton extends StatelessWidget {
PurchaseButton(
this.product,
this.timePeriod, {
@required this.subscription,
@required this.purchaseStarted,
@required this.purchaseCompleted,
required this.subscription,
required this.purchaseStarted,
required this.purchaseCompleted,
});
@override
@ -36,7 +34,7 @@ class PurchaseButton extends StatelessWidget {
String text;
if (product != null) {
text = tr("widgets.PurchaseButton.text", namedArgs: {
'price': product.price,
'price': product!.price,
});
if (subscription) {
text += '/ $timePeriod';
@ -56,11 +54,11 @@ class PurchaseButton extends StatelessWidget {
Future<void> _initPurchase(BuildContext context) async {
var pm = await PurchaseManager.init();
if (pm == null) {
purchaseCompleted(PurchaseManager.error, null);
purchaseCompleted(PurchaseManager.error!, null);
return;
}
var sentSuccess = await pm.buyNonConsumable(product, purchaseCompleted);
var sentSuccess = await pm.buyNonConsumable(product!, purchaseCompleted);
purchaseStarted(sentSuccess);
/*
@ -95,10 +93,10 @@ class PurchaseWidget extends StatefulWidget {
final bool isSubscription;
PurchaseWidget({
@required this.skus,
@required this.defaultSku,
required this.skus,
required this.defaultSku,
this.timePeriod = "",
@required this.isSubscription,
required this.isSubscription,
});
@override
@ -106,8 +104,8 @@ class PurchaseWidget extends StatefulWidget {
}
class _PurchaseWidgetState extends State<PurchaseWidget> {
List<ProductDetails> _products;
ProductDetails _selectedProduct;
List<ProductDetails>? _products;
ProductDetails? _selectedProduct;
String error = "";
bool _pendingPurchase = false;
@ -122,11 +120,11 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
var pm = await PurchaseManager.init();
if (pm == null) {
setState(() {
error = PurchaseManager.error;
error = PurchaseManager.error!;
});
}
final response = await pm.queryProductDetails(widget.skus);
final response = await pm!.queryProductDetails(widget.skus);
if (response.error != null) {
Log.e("IAP queryProductDetails: ${response.error}");
}
@ -134,17 +132,19 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
if (!mounted) return;
var products = response.productDetails;
/*
Log.i("Products: ${products.length}");
for (var p in products) {
Log.i("Product ${p.id} -> ${p.price}");
}
*/
setState(() {
_products = products;
_selectedProduct = _products.isNotEmpty ? _products.first : null;
_selectedProduct = _products!.isNotEmpty ? _products!.first : null;
if (_products.length > 1) {
for (var p in _products) {
if (_products!.length > 1) {
for (var p in _products!) {
if (p.id == widget.defaultSku) {
_selectedProduct = p;
break;
@ -169,8 +169,8 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
: buildBody(context);
}
ProductDetails _fromPaymentInfo(PaymentInfo info) {
for (var p in _products) {
ProductDetails? _fromPaymentInfo(PaymentInfo info) {
for (var p in _products!) {
if (p.id == info.id) {
return p;
}
@ -181,8 +181,8 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
Widget buildBody(BuildContext context) {
var slider = PurchaseSlider(
values: _products.map(PaymentInfo.fromProductDetail).toList(),
selectedValue: PaymentInfo.fromProductDetail(_selectedProduct),
values: _products!.map(PaymentInfo.fromProductDetail).toList(),
selectedValue: PaymentInfo.fromProductDetail(_selectedProduct!),
onChanged: (PaymentInfo info) {
setState(() {
_selectedProduct = _fromPaymentInfo(info);
@ -231,27 +231,27 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
);
}
ProductDetails _prevProduct() {
for (var i = 0; i < _products.length; i++) {
if (_products[i] == _selectedProduct) {
return i > 0 ? _products[i - 1] : _products[i];
ProductDetails? _prevProduct() {
for (var i = 0; i < _products!.length; i++) {
if (_products![i] == _selectedProduct) {
return i > 0 ? _products![i - 1] : _products![i];
}
}
return null;
}
ProductDetails _nextProduct() {
for (var i = 0; i < _products.length; i++) {
if (_products[i] == _selectedProduct) {
return i < _products.length - 1 ? _products[i + 1] : _products[i];
ProductDetails? _nextProduct() {
for (var i = 0; i < _products!.length; i++) {
if (_products![i] == _selectedProduct) {
return i < _products!.length - 1 ? _products![i + 1] : _products![i];
}
}
return null;
}
void _purchaseCompleted(String err, SubscriptionStatus subStatus) {
void _purchaseCompleted(String err, SubscriptionStatus? subStatus) {
if (!mounted) return;
if (err.isEmpty) {
@ -275,9 +275,9 @@ class _PurchaseWidgetState extends State<PurchaseWidget> {
class _PurchaseSliderButton extends StatelessWidget {
final Widget icon;
final Function onPressed;
final void Function() onPressed;
_PurchaseSliderButton({@required this.icon, @required this.onPressed});
_PurchaseSliderButton({required this.icon, required this.onPressed});
@override
Widget build(BuildContext context) {