Implementing in-app purchases into your Flutter app
Updated: October 11, 2024
13 min read
Flutter is a relatively new framework developed by Google that lets you quickly create cross-platform apps. The other popular framework is React Native by Meta. Flutter apps are built for both iOS and Android at once. Therefore, a purchase library must be compatible with both StoreKit and Billing Library.
Architecture-wise, any payment plugin – including our Adapty Flutter SDK – is a wrapper around the native libraries, StoreKit, and Billing Library. In our case, it’s a wrapper around our own Adapty iOS and Android SDK libraries.
Open-source libraries for in-app purchases in Flutter-based apps
Popular solutions for purchases in Flutter apps are the open-source plugins in_app_purchase (developed by the team behind Flutter) and flutter_inapp_purchase (non-official plugin).
These plugins were made to implement client-side purchases. They don’t feature server-side receipt validation. You’ll need to set up your own server-side infrastructure to validate receipts and collect payment analytics concerning renewals, refunds, trials, cancellations, and more.
What’s more, these libraries are usually slow to support new store features. Just to name a few of these, right now, they lack promo offers, pay-as-you-go features, and the pay upfront feature for iOS.
Since our library talks to our server, it features all of these:
- Purchase server validation
- Full native payment features support
- DRY syntax
- Subscriptions and purchases analytics and some other data collection
- A/B tests, as well as quick experimenting with purchases, pricing, and promo periods
- Paywalls and promo offers
To keep this article brief and easy to read, we’ll link a few articles we’ve previously written about some steps you should take before you start working on purchases for your Flutter app.
1. Creating a developer account on iOS and Android
First, you’ll need to create a developer account if you don’t have one. Next, you’ll need to create a weekly purchase for both iOS and Android. We’ve explained how to do this in our previous articles:
Creating in-app purchases for iOS
Creating in-app purchases for Android
Once you’re done, you can start creating and configuring your project.
2. Creating and configuring the project
To get your purchases running both in your implementation and our system, you’ll need to fill in these technical details.
For iOS, you’ll need to:
- Specify the Bundle ID to link your Adapty project to your app;
- Set up App Store Server Notifications in App Store Connect, so that you can be notified about subscription events;
- Specify the App Store Shared Secret, which the Adapty server will use to establish a connection to Apple’s servers and validate receipts.
For Android, both Package Name and Service Account Key File must be specified. The package name is the iOS Bundle ID for Android. You need to use the same one that’s used in the code. It can be found in the /android/app/build.gradle file, which is situated in the following directory:
android.defaultConfig.applicationId
3. Configuring products and paywalls
The Adapty product encapsulates those of various stores. It’s just the App Store and Google Play for now, but we’ll introduce other stores in the future. The idea behind this approach is to make cross-platform stats collection easier, as well as let you work with top-level entities, as opposed to identifiers.
Paywall is an abstract entity that contains a product array and a remote config file (which is a JSON file containing the metadata you specified in your dashboard). You hard code the paywall ID into your app – this ID is used to retrieve the config file and product data. You can edit both of these without having to update the app. In the usual scenario, you design the paywall and fill it with the data received from us.
Creating a product
Let’s use Google Play Console and App Store Connect to create a product that corresponds to a weekly subscription. Fill in the ID of the corresponding products, which you can retrieve from payment systems. It’s important to note that since App Store Connect has no API, you’ll need to do this manually. Just once, though.
Creating a paywall
When creating a paywall, the key thing is to specify its ID in a convenient format. This identifier will later be used by the SDK to request the paywall data. We believe this to be a good approach: with this architecture, there’s no need to hard code your products on the client side. You can be flexible with your product management, versioning, tests, etc. The alternative option would be Firebase JSON with a set of built-in products. This doesn’t provide error validation and isn’t as user-friendly, though.
Designing the paywall
At this stage, it may be helpful to already think about designing your paywall. There are different approaches on how to create a profitable and good-looking paywall, but usually, it requires some experienced design work and more coding as well. To make this step easier, you can use the paywall builder featured in Adapty. It currently supports only iOS SDK but later will be available for other platforms as well.
It’s a user-friendly editor for creating and editing paywalls on the fly, without any coding or design work required. In a few simple steps, you’d be able to create a native paywall with a beautiful background, headline and a list of features, set of subscription products, manually chosen color scheme, and adjustable CTA button.
The JSON editor, which comes hand-in-hand with the paywall builder, also allows you to localize your paywall in different languages. This means that you can localize all your paywall texts (header, premium features, CTA button text, etc.) using one JSON field.
Now ready for your first purchase, so let’s proceed to installing the SDK.
4. How to use the Adapty SDK for Flutter
Let’s look at the main functions we’ll need to configure and use subscriptions.
Installing the library
First, you’ll need to add the adapty_flutter library to your project. To do that, add the following dependency to your pubspec.yaml
file:
dependencies:
adapty_flutter: ^2.4.1
After that, run:
flutter pub get
From here, you can import Adapty SDK into your app like this:
import 'package:adapty_flutter/adapty_flutter.dart';
Configuration
iOS: Create Adapty-Info.plist
and add it to you project. Add the AdaptyPublicSdkKey
in this file with the value of your Public SDK key.
Android: add the AdaptyPublicSdkKey
into AndroidManifest.xml
(for Android).
You can find your SDK key in Adapty settings:
Adapty-Info.plist:
<dict>
...
<key>AdaptyPublicSdkKey</key>
AndroidManifest.xml:
<application ...>
...
<meta-data
android:name="AdaptyPublicSdkKey"
android:value="PUBLIC_SDK_KEY" />
</application>
Next, activate the SDK by invoking this code on the Flutter side:
void main() {
runApp(MyApp());
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
Adapty().setLogLevel(AdaptyLogLevel.verbose);
Adapty().activate();
super.initState();
}
// ... _MyAppState implementation ...
}
Adapty logs error messages and other important data to help you understand what’s going on. The function lets you choose one of the five possible values:
Future<void> setLogLevel(AdaptyLogLevel value)
You can read more here.
Adapty creates an internal profile ID for every user. However, if you have your own authentication system, you should set your own Customer User ID:
Future<bool> Adapty.identify(String customerUserId)
You can read more here.
Retrieving paywalls
To retrieve the paywall, run this code:
try {
final paywall = await Adapty().getPaywall(id: "YOUR_PAYWALL_ID", locale: "en");
// the requested paywall
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
Once you have the paywall, you can query the product array that corresponds to it:
Next, build your paywall view using the fetched products and show it to the user.
Making purchases
In the previous step, you’ve retrieved the paywall. Now, we’ll look for the one you need to display those products in your UI:
try {
final products = await Adapty().getPaywallProducts(paywall: paywall);
// the requested products array
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
Next, using the product array, display all of its items. It also very important to correctly display introductory offers. Read more here.
Let’s say the user wants to buy the first product. For the sake of simplicity, we’ll assume it to be the first item of the product array.
To initiate the purchase, invoke the makePurchase
function. (Remember to wrap this in a try-catch block to still receive all error messages from the SDK):
final product = products.first; // don't forget to check the List is not empty
try {
final profile = await Adapty().makePurchase(product: product);
// successful purchase
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
Once the function has been successfully executed, the result variable will contain an actual user profile. Here’s how to check the access level after the purchase has been completed:
if (profile?.accessLevels['premium']?.isActive ?? false) {
// grant access to premium features
}
Note that AdaptyErrorCode.paymentCancelled
means the user canceled the purchase themselves, which means it isn’t an actual error message.
To restore purchases, use the .restorePurchases()
method:
try {
final profile = await Adapty().restorePurchases();
// check the access level
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
Take into account that both results from the .makePurchase()
and .restorePurchases()
contains AdaptyProfile
. This object contains data about access levels, subscriptions, and purchases. Usually, you can tell whether the user can access the premium features of your app or not, just by checking their access level.
Subscription status
To avoid having to check through the entire preceding transaction chain, we’re introducing the term “access level.” Access level is a flag that explains how much of the app’s functionality the user can access. If they haven’t made any purchases yet, then their access level will be null. Otherwise, it’ll be what you specified for your product.
For example, you can have two access levels, namely, silver and golden. Different purchases will unlock different access levels and features. Most apps have just one access level.
To see whether the subscription is active, it’s enough to check if the user has an active access level. You can use the .getProfile()
method to do just that:
try {
final profile = await Adapty().getProfile();
// check the access level
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
}
You can also subscribe to the .didUpdateProfileStream
stream to timely learn of any changes in the subscriber’s access level. Here’s how:
Adapty().didUpdateProfileStream.listen((profile) {
// handle any changes to subscription state
});
Conclusion
We’ve designed our SDK to help you quickly integrate payments into your app. What’s more, we’ve also tried to simplify all the other steps you might need to take, such as A/B testing, analytics, and further integrations.
We’ll provide full purchase functionality free of charge if your revenue amounts to less than $10,000 per month. Save yourself months of work and focus on the most important thing – your product.