Skip to content

Instantly share code, notes, and snippets.

@axilaris
Last active June 15, 2020 05:12
Show Gist options
  • Save axilaris/2b186c7a4073671128e8cacc09dfc384 to your computer and use it in GitHub Desktop.
Save axilaris/2b186c7a4073671128e8cacc09dfc384 to your computer and use it in GitHub Desktop.
class PurchaseDialog extends StatefulWidget with NavigationStates {
@override
_PurchaseDialogState createState() => _PurchaseDialogState();
PurchaseDialog();
}
class _PurchaseDialogState extends State<PurchaseDialog> {
_PurchaseDialogState();
String title = "Pro Version";
String description = "Full App Access";
String purchaseButtonText = "Loading...";
bool isLoading = true;
// inapp
StreamSubscription _purchaseUpdatedSubscription;
StreamSubscription _purchaseErrorSubscription;
StreamSubscription _conectionSubscription;
final List<String> _productLists = Platform.isAndroid
? [
// 'android.test.purchased',
// 'android.test.canceled',
'example_pro_inapp_android',
]
: ['example_pro_inapp'];
//
String _platformVersion = 'Unknown';
List<IAPItem> _items = [];
List<PurchasedItem> _purchases = [];
static const double padding = 20.0;
final primaryColor = const Color(0xFF75A2EA);
final grayColor = const Color(0xFF939393);
void _requestPurchase(IAPItem item) {
FlutterInappPurchase.instance.requestPurchase(item.productId);
}
Future _getProduct() async {
print("_getProduct");
List<IAPItem> items;
try {
items = await FlutterInappPurchase.instance.getProducts(_productLists);
print('flutterInappPurchase.instance.getProducts');
} catch (err) {
print('flutterInappPurchase.instance.getProducts error: $err');
Navigator.of(context).pop();
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Failure",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 33,
fontWeight: FontWeight.w800,
color: Color(0xFFF44336 ),
),),
content: new Text("Error. Possibly no internet. Please turn on your internet.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
return;
}
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
print("items.length");
print(items.length);
if (items.length > 0)
{
if (items[items.length-1].title == "")
{
print("empty title");
}
title = items[items.length-1].title;
description = items[items.length-1].description + "\n" + items[items.length-1].localizedPrice;
purchaseButtonText = "Purchase";
isLoading = false;
}
setState(() {
this._items = items;
this._purchases = [];
});
}
Future _getPurchases() async {
List<PurchasedItem> items =
await FlutterInappPurchase.instance.getAvailablePurchases();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
Future _getPurchaseHistory() async {
print("_getPurchaseHistory");
List<PurchasedItem> items = await FlutterInappPurchase.instance.getPurchaseHistory();
print(items.length);
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initInAppPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await FlutterInappPurchase.instance.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// prepare
var result = await FlutterInappPurchase.instance.initConnection;
print('result: $result');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
// refresh items for android
try {
String msg = await FlutterInappPurchase.instance.consumeAllItems;
print('consumeAllItems: $msg');
} catch (err) {
print('consumeAllItems error: $err');
}
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) async {
print('purchase-updated: $productItem');
bool result = await savePurchaseStatusPreference(true);
print(isPurchasedObject.isPurchased);
Navigator.of(context).pop();
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
print('purchase-error: $purchaseError');
Navigator.of(context).pop();
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Failure",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 33,
fontWeight: FontWeight.w800,
color: Color(0xFFF44336 ),
),),
content: new Text("Error Purchase",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
});
_getProduct();
}
@override
void initState(){
super.initState();
initInAppPlatformState();
}
@override
void dispose(){
super.dispose();
}
void purchaseOnPressedAction() async
{
print("purchaseOnPressedAction");
if (_items.length > 0)
{
print(_items[_items.length-1].productId);
_requestPurchase(_items[_items.length-1]);
}
else
{
print("error nothing to purchase");
Navigator.pop(context);
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Failure",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 33,
fontWeight: FontWeight.w800,
color: Color(0xFFF44336 ),
),),
content: new Text("Error - Nothing To Purchase",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(padding),
),
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.all(padding),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(padding),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 10.0,
offset: const Offset(0.0, 10.0),
),
]),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 24.0),
AutoSizeText(
title,
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(
color: primaryColor,
fontSize: 25.0,
),
),
SizedBox(height: 24.0),
AutoSizeText(
description,
maxLines: 4,
textAlign: TextAlign.center,
style: TextStyle(
color: grayColor,
fontSize: 18.0,
),
),
SizedBox(height: 24.0),
RaisedButton(
color: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0)),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
child: AutoSizeText(
purchaseButtonText,
maxLines: 1,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
color: Colors.white,
),
),
),
onPressed: (isLoading) ? null : () => purchaseOnPressedAction(),
),
SizedBox(height: 10.0),
showSecondaryButton(context),
],
),
)
],
),
);
}
showSecondaryButton(BuildContext context) {
return FlatButton(
child: AutoSizeText(
"Cancel",
maxLines: 1,
style: TextStyle(
fontSize: 18,
color: primaryColor,
fontWeight: FontWeight.w400,
),
),
onPressed: () {
print("secondary button pressed");
Navigator.of(context).pop();
},
);
}
}
@axilaris
Copy link
Author

notice in the last part:

showSecondaryButton(BuildContext context) {
....
Navigator.of(context).pop(); <--- this will cause the error below. infact, any pop in the code will cause the same error

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method 'findAncestorStateOfType' was called on null.

@axilaris
Copy link
Author

axilaris commented Jun 15, 2020

PurchaseDialog above is called in this way:

    showDialog(
      context: context,
      builder: (BuildContext context) => PurchaseDialog(),
    ).then((value) {
      setState(() {
      });
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment