Flutter In App Purchases paying for App features and Unlock them | Non Consumable.
Non-consumables are purchased once by users. Services that do not expire or decrease with use are usually implemented as non-consumables, such as removing ads from App for users.
In this tutorial, we will cover how to Implement Non-Consumables using the in_app_purchase plugin in your flutter App.
1- Add these permissions to
android/app/src/main/AndroidManifest.xml
1 2 | <uses-permission android:name="com.android.vending.BILLING" /> <uses-permission android:name="android.permission.INTERNET"/> |
2- Add this line in the dependencies block of your project's build.gradle.
android/app/build.gradle
1 2 3 4 5 | dependencies {implementation 'com.android.billingclient:billing:3.0.2'} |
3- Make sure you are using your own product ID
4- Add this plugin to your package’s pubspec.yaml file:
1 | 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 | import 'dart:async';import 'package:flutter/material.dart';import 'package:in_app_purchase/in_app_purchase.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(); super.initState(); } @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 where we show our pay button: 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 | 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.buyNonConsumable(purchaseParam: purchaseParam); } @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: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text(provider.available ? "Store is Available" : "Store is not Available", style: TextStyle(fontSize: 22, color: Colors.green),), ], ), ), ), ), provider.isPurchased ? Row(children: [Text("Features Unlocked"),Icon(Icons.lock_open) ] ,):Row(children: [Text("Features Locked"),Icon(Icons.lock)],), for (var prod in provider.products) if (provider.hasPurchased(prod.id) != null) ...[ Center( child: FittedBox( child: Text( 'THANK YOU!', textAlign: TextAlign.center, style: TextStyle(fontSize: 60, color: Colors.black), ), ), ), Container( height: 50, ), ]else ...[ Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Unlock Features pay ${prod.price}", style: TextStyle(fontSize: 22, color: Colors.black54), textAlign: TextAlign.center, ), FlatButton( onPressed: () => _buyProduct(prod), child: Text('Pay'), color: Colors.green, ), ], ), ), ] ], ), ), ), ); }} |
We are going to use provider state management so add this to your package’s pubspec.yaml file:
provider: ^4.3.1This 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 | import 'dart:async';import 'dart:io';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 = 'in_app_payment_test'; bool _isPurchased = false; bool get isPurchased => _isPurchased; set isPurchased(bool value) { _isPurchased = 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; } } } } 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; }} |