Comprehensive Guide to In-App Purchases in Flutter for Android and iOS

In-app purchases play a pivotal role in the revenue model of many mobile applications. Flutter, a popular open-source framework for building cross-platform mobile applications, provides a straightforward way to integrate in-app purchases seamlessly into your Android and iOS apps. This comprehensive guide will cover all the essential aspects of implementing in-app purchases in Flutter, including setting up your project, handling purchases, and testing.
Getting Started
Setting up in-app purchases involves configurations on both the Google Play Console (for Android) and App Store Connect (for iOS). Below are the steps for both platforms:
1. Google Play Console (Android)
1. Create A New App:
- Go to the Google Play Console.
- Click on “Create App” and follow the prompts to create a new app.
2. Add Products:
- In the left menu, go to “Monetize” and select “In-app Products.”
- Click on “+ Add New Product” to add a new in-app product.
- Choose the type of product (e.g., in-app purchase) and fill in the required details, including the product ID, price, and description.
3. Set Up Licensing:
- Go to “Development Tools” > “Services & APIs.”
- Ensure that the “Licensing & in-app billing” checkbox is checked.
4. Obtain Billing Key:
- Under “Development Tools,” click on “Services & APIs.”
- Find your license key (billing key) and use it in your Flutter app as mentioned in the Android configuration steps.
2. App Store Connect (iOS)
1. Create a New App:
- Go to App Store Connect.
- Click on the “+” button to create a new app.
2. Add In-App Purchases:
- In the left menu, go to “My Apps” and select your app.
- Navigate to “App Store” > “In-App Purchases.”
- Click on the “+” button to add a new in-app purchase.
- Choose the type of in-app purchase and fill in the necessary details, including the product ID, reference name, and pricing.
3. Set Up Sandbox Testers:
- In the left menu, go to “Users and Access” > “Sandbox Testers.”
- Add test accounts that you’ll use for testing in-app purchases.
4. Generate App-Specific Shared Secret:
- In the left menu, go to “App Store” > “App Information.”
- Scroll down to the “App-Specific Shared Secret” section and click on “Generate.”
5. Add SKAdNetwork Items (Optional):
- As mentioned in the iOS configuration steps, add SKAdNetwork items to your Info.plist file based on the ad network IDs you use.
3. Additional Tips:
- Testing In-App Purchases:
- For Android, use the license tester accounts set up in the Play Console.
- For iOS, use the sandbox tester accounts created in App Store Connect.
2. Release and Review:
- Before releasing your app, thoroughly test in-app purchases to ensure a smooth user experience.
- On the App Store, in-app purchases might undergo a review process. Ensure that your in-app purchase content complies with Apple’s guidelines.
Remember to consult the official documentation for both platforms for the most up-to-date information:
Project Setup
Start by creating a new Flutter project using the following commands in your terminal:
flutter create my_in_app_purchase_app
cd my_in_app_purchase_appNext, open the pubspec.yaml file and add the in_app_purchase package:
dependencies:
in_app_purchase: ^0.6.0Now, run flutter pub get to fetch the package.
1. Android Configuration
For Android, you need to add the following configuration in the android/app/build.gradle file:
android {
...
// Add the following block
buildTypes {
release {
signingConfig signingConfigs.release
// Add the next two lines
resValue "string", "play_billing_key", 'your_play_billing_key'
resValue "string", "play_billing_key_public", 'your_play_billing_key_public'
}
}
}Replace 'your_play_billing_key' and 'your_play_billing_key_public' with the actual values obtained from the Google Play Console.
2. iOS Configuration
For iOS, open the ios/Runner/Info.plist file and add the following:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>your_ad_network_id</string>
</dict>
</array>Replace 'your_ad_network_id' with the actual value.
Implementing In-App Purchases
1. Initializing the Plugin
Open the main.dart file and initialize the in_app_purchase plugin:
import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
// Initialize in_app_purchase
InAppPurchaseConnection.enablePendingPurchases();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('In-App Purchases'),
),
body: Center(
child: Text('Welcome to the In-App Purchases Demo!'),
),
);
}
}2. Displaying Products
To display available products for purchase, modify the _HomePageState class:
class _HomePageState extends State<HomePage> {
List<ProductDetails> _products = [];
@override
void initState() {
super.initState();
// Initialize in_app_purchase
InAppPurchaseConnection.enablePendingPurchases();
// Load products
_loadProducts();
}
Future<void> _loadProducts() async {
final ProductDetailsResponse response = await InAppPurchaseConnection.instance.queryProductDetails(Set.from(['your_product_id']));
if (response.notFoundIDs.isNotEmpty) {
print('Error: Some products not found');
return;
}
setState(() {
_products = response.productDetails;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('In-App Purchases'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Available Products:'),
Column(
children: _products.map((product) {
return ListTile(
title: Text(product.title),
subtitle: Text(product.description),
trailing: ElevatedButton(
onPressed: () {
// Purchase logic goes here
},
child: Text('Buy'),
),
);
}).toList(),
),
],
),
),
);
}
}This code fetches the details of a product with the specified product ID and displays them on the screen. The ‘Buy’ button currently lacks functionality, which we’ll implement next.
3. Making a Purchase
Update the _HomePageState class to include purchase logic:
class _HomePageState extends State<HomePage> {
// Existing code...
Future<void> _buyProduct(ProductDetails product) async {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
bool canMakePurchase = await InAppPurchaseConnection.instance.isAvailable();
if (canMakePurchase) {
await InAppPurchaseConnection.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
print('Error: In-app purchases are not available.');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
// Existing code...
Column(
children: _products.map((product) {
return ListTile(
title: Text(product.title),
subtitle: Text(product.description),
trailing: ElevatedButton(
onPressed: () {
_buyProduct(product);
},
child: Text('Buy'),
),
);
}).toList(),
),
);
}
}Now, the ‘Buy’ button calls the _buyProduct method when pressed, initiating the purchase process.
4. Handling Purchases
Add a listener to handle successful purchases and updates to purchases. Modify the _HomePageState class:
class _HomePageState extends State<HomePage> {
// Existing code...
@override
void initState() {
super.initState();
// Existing code...
// Listen for purchases
InAppPurchaseConnection.instance.purchaseUpdatedStream.listen((List<PurchaseDetails> purchaseDetailsList) {
_handlePurchases(purchaseDetailsList);
});
}
Future<void> _handlePurchases(List<PurchaseDetails> purchaseDetailsList) async {
for (PurchaseDetails purchaseDetails in purchaseDetailsList) {
if (purchaseDetails.status == PurchaseStatus.purchased) {
// Purchase successful, implement logic here
print('Purchase successful: ${purchaseDetails.productID}');
}
}
}
// Existing code...
}This code listens to updates in the purchase stream and calls the _handlePurchases method when a purchase is detected. The _handlePurchases method checks if the purchase status is 'purchased' and executes the purchase logic.
Testing In-App Purchases
Testing in-app purchases requires a real device or an emulator. You can use the following steps to test your implementation:
- Android Testing: Upload a draft version of your app to the Play Store’s internal testing track. Use a test account with the license tester role.
- iOS Testing: Create a sandbox tester account in App Store Connect and use it to test in-app purchases in the iOS simulator.
Remember to follow platform-specific guidelines for testing in-app purchases on Android and iOS.
Conclusion
Integrating in-app purchases in Flutter for Android and iOS involves several steps, from project setup to handling purchases. This guide covers the essential aspects, including initializing the plugin, displaying products, making purchases, and handling successful purchases. Make sure to follow platform-specific guidelines and thoroughly test your in-app purchases to provide a seamless and reliable user experience.

