In-App Purchase Consumable in Flutter with donations and rewards examples.
In-App Purchase Consumable in Flutter with donations and rewards examples.
In this flutter article, we will implement consumables using in-app purchases package using donation and rewards examples. Consumable products can be purchased multiple times they can be used for like donations and buying coins for digital goods.
1- Add these permissions to
android/app/src/main/AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.INTERNET"/>
2- Add this line in the dependencies block
android/app/build.gradle
dependencies {
implementation 'com.android.billingclient:billing:3.0.2'
}
3- Make sure you are using your own product ID
4- Add this to your package’s pubspec.yaml file:
in_app_purchase: ^0.5.2
This is our file: main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | import 'package:flutter/material.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'homescreen.dart'; import 'package:provider/provider.dart'; import 'providermodel.dart'; void main() { InAppPurchaseConnection.enablePendingPurchases(); runApp(ChangeNotifierProvider( create: (context) => ProviderModel(), child: MaterialApp(home: MyApp(),))); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override void initState() { var provider = Provider.of<ProviderModel>(context, listen: false); provider.initialize(); _getValueFromShared(provider); super.initState(); } _getValueFromShared(provider) async { SharedPreferences prefs = await SharedPreferences.getInstance(); int coinValue = prefs.getInt('coins') ?? 0; bool isPurchased = prefs.getBool('isPurchased') ?? false; provider.coins = coinValue; provider.isPurchased = isPurchased; } @override void dispose() { var provider = Provider.of<ProviderModel>(context, listen: false); provider.subscription.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return HomeScreen(); } } |
Here is our first Screen UI file: homescreen.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import 'package:flutter/material.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:provider/provider.dart'; import 'providermodel.dart'; class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { InAppPurchaseConnection _iap = InAppPurchaseConnection.instance; @override void initState() { var provider = Provider.of<ProviderModel>(context, listen: false); provider.verifyPurchase(); super.initState(); } void _buyProduct(ProductDetails prod) { final PurchaseParam purchaseParam = PurchaseParam(productDetails: prod); _iap.buyConsumable(purchaseParam: purchaseParam, autoConsume: true); } @override Widget build(BuildContext context) { var provider = Provider.of<ProviderModel>(context); return Scaffold( appBar: AppBar( title: Text("In App Purchase"), ), body: Center( child: Container( height: 500, width: 200, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( child: Padding( padding: const EdgeInsets.all(8.0), child: Container(color: Colors.grey.withOpacity(0.2), child: FittedBox( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text(provider.available ? "Store is Available" : "Store is not Available", style: TextStyle(fontSize: 22, color: Colors.green),), ], ), ), ), ), ), for (var prod in provider.products) Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Donate / Buy Coins ${prod.price}", style: TextStyle(fontSize: 22, color: Colors.black54), textAlign: TextAlign.center, ), FlatButton( onPressed: () => _buyProduct(prod), child: Text('Donate / Buy Coins'), color: Colors.green, ), ], ), ), provider.isPurchased?Center( child: FittedBox( child: Text( 'THANK YOU Greatly\nappreciate your\n<img draggable="false" role="img" class="emoji" alt=" textAlign: TextAlign.center, style: TextStyle(fontSize: 20, color: Colors.black), ), ), ):SizedBox.shrink(), Center( child: FittedBox( child: Text( '<img draggable="false" role="img" class="emoji" alt=" textAlign: TextAlign.center, style: TextStyle(fontSize: 60, color: Colors.black), ), ), ), ], ), ), ), ); } } |
We are going to use provider state management and Shared preferences to save app status so add this to your package’s pubspec.yaml file:
provider: ^4.3.1
shared_preferences: ^2.0.5
This is our provider model class: providermodel.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import 'dart:async'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; class ProviderModel with ChangeNotifier { InAppPurchaseConnection _iap = InAppPurchaseConnection.instance; bool available = true; StreamSubscription subscription; final String myProductID = 'consumable_product'; bool _isPurchased = false; bool get isPurchased => _isPurchased; set isPurchased(bool value) { _isPurchased = value; notifyListeners(); } int _coins = 0; int get coins => _coins; set coins(int value) { _coins = value; notifyListeners(); } List _purchases = []; List get purchases => _purchases; set purchases(List value) { _purchases = value; notifyListeners(); } List _products = []; List get products => _products; set products(List value) { _products = value; notifyListeners(); } void initialize() async { available = await _iap.isAvailable(); if (available) { await _getProducts(); await _getPastPurchases(); verifyPurchase(); subscription = _iap.purchaseUpdatedStream.listen((data) { purchases.addAll(data); verifyPurchase(); }); } } void verifyPurchase() { PurchaseDetails purchase = hasPurchased(myProductID); if (purchase != null && purchase.status == PurchaseStatus.purchased) { if (purchase.pendingCompletePurchase) { _iap.completePurchase(purchase); if (purchase != null && purchase.status == PurchaseStatus.purchased) { isPurchased = true; _coins += 10; } _saveValueInShared(); } } } _saveValueInShared() async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setInt('coins', coins); prefs.setBool('isPurchased', isPurchased); } PurchaseDetails hasPurchased(String productID) { return purchases .firstWhereOrNull((purchase) => purchase.productID == productID); } Future<void> _getProducts() async { Set<String> ids = Set.from([myProductID]); ProductDetailsResponse response = await _iap.queryProductDetails(ids); products = response.productDetails; } Future<void> _getPastPurchases() async { QueryPurchaseDetailsResponse response = await _iap.queryPastPurchases(); for (PurchaseDetails purchase in response.pastPurchases) { if (Platform.isIOS) { _iap.consumePurchase(purchase); } } purchases = response.pastPurchases; } } |