mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-26 08:36:50 +08:00
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:
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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),
|
||||
],
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user