LinkFive - Successful Subscriptions
Flutter Provider Plugin Example
Flutter Provider Plugin Example
Let's get right into the provider example.
We also have a Flutter provider plugin on pub.dev
Create a new Provider class
We're using a ChangeNotifier to notify all listeners that something has changed.
class LinkFiveProvider extends ChangeNotifier {
}
We will initialize LinkFive in it's constructor. This will also check if an active subscription is currently existing.
We will also listen for all data changes that is coming through the plugin with a listener.
- SubscriptionData: Is the subscription product which contains the price, the duration and the name of your subscriptions. This data is used to offer a subscription product to your users.
- ActiveSubscriptionData: Is an active verified subscription that the user purchased.
LinkFiveProvider() { LinkFivePurchases.init("API_KEY"); LinkFivePurchases .products .listen(_productsUpdate) LinkFivePurchases .activeProducts .listen(_activeProductsUpdate)}
Inside the listener methods, you can just save the data to the ChangeNotifier and notify all Listeners that something has changed.
void _productsUpdate(LinkFiveProducts data) async { this.products = data; notifyListeners();}
void _activeProductsUpdate(LinkFiveActiveProducts data) { activeProducts = data; notifyListeners();}
This was the basic example and should be enough to implement subscriptions into your App. You should also save the streams to the ChangeNotifier and cancel them on dispose.
Add your Provider to your other providers
In your main method or wherever you want to add the provider to the Widget tree, add the provider like any other providers.
MultiProvider(providers: [ ChangeNotifierProvider( create: (context) => LinkFiveProvider(), lazy: false, ), // Your other providers])
Consume the Provider
In your build method, right before you want to offer the subscriptions, you can consume the provider using a Consumer< LinkFiveProvider >
Widget build(BuildContext context) { return Container( child: Consumer<LinkFiveProvider>( builder: (_, linkFiveProvider, __) { var products = linkFiveProvider.products; if (products != null) {
// use the subscription Data to build your Purchase buttons
// example: return Column( children: [ Row( children: buildSubscriptionButtons(products), mainAxisAlignment: MainAxisAlignment.spaceEvenly, ) ], ); } return Center(child: Text('No Subscription found...')); }, ));}
In your buildSubscriptionButtons method, loop through the subscriptions and create a button for each element.
buildSubscriptionButtons(LinkFiveProducts linkFiveProducts) { return linkFiveProducts.productDetailList .map((linkFiveProductDetails) => ElevatedButton(
onPressed: () { LinkFivePurchases.purchase(linkFiveProductDetails.productDetails); },
child: Text(linkFiveProductDetails.productDetails.price), )) .toList();}
When the user presses the button, LinkFivePurchases.purchase(...) is called and handles the entire purchase process.
The verified receipt will be passed to the activeSubscription stream.
Call .fetchProducts()
To receive your product offerings, you should call fetchProducts to load all available subscriptions. We did not call it automatically since you might want to avoid calling to fetch the latest products if the user already has an active premium plan.
Another good practice is to
Complete Provider Example
Now also as a standalone package: linkfive_purchases_provider
import 'dart:async';
import 'package:flutter/material.dart';import 'package:linkfive_purchases/linkfive_purchases.dart';
/// LinkFive Provider////// Initialize LinkFive with your Api Key////// Please register on our website: https://www.linkfive.io to get an api keyclass LinkFiveProvider extends ChangeNotifier { /// LinkFive client as factory final LinkFivePurchasesMain linkFivePurchases = LinkFivePurchasesMain();
/// LinkFive subscriptions that you can offer to your user LinkFiveProducts? products;
/// All verified receipt received by LinkFive LinkFiveActiveProducts? activeProducts;
/// Streams that will be cleaned on dispose List<StreamSubscription> _streams = [];
/// All verified receipts as List or emptyList List<LinkFivePlan> get activePlanList => activeProducts?.planList ?? [];
/// LinkFive as CallbackInterface for your Paywall CallbackInterface get callbackInterface => linkFivePurchases;
/// conveniently check if the user has any activeProducts bool get hasActiveProduct => activeProducts != null && activeProducts!.planList.isNotEmpty;
/// Initialize LinkFive with your Api Key /// /// Please register on our website: https://www.linkfive.io to get an api key /// /// [LinkFiveEnvironment] is 99,999..% [LinkFiveEnvironment.PRODUCTION] better not touch it LinkFiveProvider(String apiKey, {LinkFiveEnvironment environment = LinkFiveEnvironment.PRODUCTION}) { linkFivePurchases.init(apiKey, env: environment); _streams.add(linkFivePurchases.products.listen(_productsUpdate)); _streams.add( linkFivePurchases.activeProducts.listen(_activeProductsUpdate)); }
/// Saves available Subscriptions and notifies all listeners void _productsUpdate(LinkFiveProducts data) async { products = data; notifyListeners(); }
/// Saves active Subscriptions and notifies all listeners void _activeProductsUpdate(LinkFiveActiveProducts data) { activeProducts = data; notifyListeners(); }
/// Fetch all available Subscription for purchase for the user /// /// The provider will notify you for changes Future<LinkFiveProducts?> fetchProducts() { return LinkFivePurchases.fetchProducts(); }
/// Restore Subscriptions of the user /// /// The provider will notify you if there is a change restoreSubscriptions() { return LinkFivePurchases.restore(); }
/// Make a Purchase /// /// The provider will notify you if there is a change /// The future returns if the "purchase screen" is visible to the user /// and not if the purchase was successful Future<bool> purchase(ProductDetails productDetail) async { return LinkFivePurchases.purchase(productDetail); }
/// Handles the Switch Plan functionality. /// /// You can switch from one Subscription plan to another. Example: from currently a 1 month subscription to a 3 months subscription /// /// on iOS: you can only switch to a plan which is in the same Subscription Family /// /// [oldPurchasePlan] given by the LinkFive Plugin /// /// [productDetails] from the purchases you want to switch to /// /// [prorationMode] Google Only: default replaces immediately the subscription, and the remaining time will be prorated and credited to the user. /// Check https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode for more information switchPlan(LinkFivePlan oldPurchasePlan, LinkFiveProductDetails productDetails, {ProrationMode? prorationMode}) { return LinkFivePurchases.switchPlan(oldPurchasePlan, productDetails, prorationMode: prorationMode); }
void dispose() async { for (var element in _streams) { await element.cancel(); } _streams = []; super.dispose(); }
/// helper function for the paywall to make it easier. /// /// returns the subscriptionDataList or if null, an empty list List<SubscriptionData> paywallUIHelperData(BuildContext context) => products?.paywallUIHelperData(context: context) ?? [];}
Use the provider and show the UI
This example uses Navigation 2.0
import 'package:flutter/material.dart';import 'package:in_app_purchases_paywall_ui/in_app_purchases_paywall_ui.dart';import 'package:linkfive_purchases_provider/linkfive_purchases_provider.dart';import 'package:provider/provider.dart';
class ProviderSimplePaywall extends StatelessWidget {
Widget build(BuildContext context) { return Consumer<LinkFiveProvider>(builder: (_, linkFiveProvider, __) { return PaywallScaffold( appBarTitle: "LinkFive Premium", child: SimplePaywall( theme: Theme.of(context), callbackInterface: linkFiveProvider.callbackInterface, subscriptionListData: linkFiveProvider.paywallUIHelperData(context), title: "Go Premium", // SubTitle subTitle: "All features at a glance", // Add as many bullet points as you like bulletPoints: [ IconAndText(Icons.stop_screen_share_outlined, "No Ads"), IconAndText(Icons.hd, "Premium HD"), IconAndText(Icons.sort, "Access to All Premium Articles") ], // Shown if isPurchaseSuccess == true successTitle: "You're a Premium User!", // Shown if isPurchaseSuccess == true successSubTitle: "Thanks for your Support!", // Widget can be anything. Shown if isPurchaseSuccess == true successWidget: Container( padding: EdgeInsets.only(top: 16, bottom: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( child: Text("Let's go!"), onPressed: () { print("let‘s go to the home widget again"); }, ) ])), tosData: TextAndUrl( "Terms of Service", "https://www.linkfive.io/tos"), // provide your PP ppData: TextAndUrl( "Privacy Policy", "https://www.linkfive.io/privacy"), // add a custom campaign widget campaignWidget: CampaignBanner( theme: Theme.of(context), headline: "🥳 Summer Special Sale", ))); }); }}