How to add In-App Purchase subscription in Flutter.
How to add In-App Purchase subscription in Flutter.
In this tutorial we will teach you how to implement subscription in your app. For this we will use the in app purchases package. In addition we will have to create a product ID for subscription in our Google Play Console which we will use in our project.
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
android/app/build.gradle inside dependencies block
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:
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 | 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: 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 113 | 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: [ !provider.isPurchased ? 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), ), ], ), ), ), ), ):SizedBox.shrink(), provider.isPurchased ? Center(child: Text("Premium Plan <img draggable="false" role="img" class="emoji" alt=" : Center( child: Text( "Get Premium Plan Subscription", style: TextStyle( fontSize: 18, backgroundColor: Colors.greenAccent), ), ), for (var prod in provider.products) if (provider.hasPurchased(prod.id) != null) ...[ Center( child: FittedBox( child: Text( 'THANK YOU! <img draggable="false" role="img" class="emoji" alt=" textAlign: TextAlign.center, style: TextStyle(fontSize: 60, color: Colors.black), ), ), ), Container( height: 50, ), ] else ...[ Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Unlock All Features Subscription: ${prod.price} per month", style: TextStyle(fontSize: 18, 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.1
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 | import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:collection/collection.dart'; class ProviderModel with ChangeNotifier { InAppPurchaseConnection _iap = InAppPurchaseConnection.instance; bool available = true; StreamSubscription subscription; final String myProductID = 'monthly_sub30'; 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; } } |