helium_flutter 0.0.3
helium_flutter: ^0.0.3 copied to clipboard
Flutter Paywall SDK for Helium. Remotely build and auto-optimize paywalls (tryhelium.com)
helium_flutter #
Installation #
Add the helium_flutter package to your pubspec.yaml:
dependencies:
helium_flutter: ^0.0.1
Then run:
flutter pub get
Make sure that Swift Package Manager Support is enabled:
flutter upgrade
flutter config --enable-swift-package-manager
See Google's guide on this for more details.
Configuration #
Set up your HeliumCallbacks #
To integrate Helium paywalls, create a class that implements the HeliumCallbacks
interface. This class is responsible for handling the purchase logic for your paywalls.
abstract class HeliumCallbacks {
// [REQUIRED] - Trigger the purchase of a product with the provided product ID.
// This method should return a HeliumTransactionStatus enum.
Future<HeliumTransactionStatus> makePurchase(String productId);
// [OPTIONAL] - Restore any existing subscriptions.
// This method should return a boolean indicating whether the restore was successful.
Future<bool> restorePurchases(bool status);
// [OPTIONAL] - Custom analytics/error logging for paywall/helium related events.
// By default, events are logged to your analytics service, but you can override
// this method to add additional custom logging/handling.
Future<void> onPaywallEvent(Map<String, dynamic> heliumPaywallEvent);
}
The HeliumTransactionStatus
enum defines the possible states of a paywall transaction:
enum HeliumTransactionStatus {
purchased,
failed,
cancelled,
restored,
pending
}
Example Callbacks Implementation: #
Basic Implementation
Here's a basic implementation of the HeliumCallbacks
interface:
import 'package:helium_flutter/helium_flutter.dart';
import 'dart:developer';
class PaymentCallbacks implements HeliumCallbacks {
@override
Future<HeliumTransactionStatus> makePurchase(String productId) async {
log('makePurchase: $productId');
// Implement your purchase logic here
return HeliumTransactionStatus.purchased;
}
@override
Future<bool> restorePurchases(bool status) async {
log('restorePurchases: $status');
// Implement your restore logic here
return status;
}
@override
Future<void> onPaywallEvent(Map<String, dynamic> heliumPaywallEvent) async {
log('onPaywallEvent: $heliumPaywallEvent');
// Handle paywall events here
}
}
RevenueCat Implementation
import 'package:helium_flutter/helium_flutter.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'dart:developer';
class RevenueCatCallbacks implements HeliumCallbacks {
@override
Future<HeliumTransactionStatus> makePurchase(String productId) async {
try {
log('RevenueCat making purchase: $productId');
final offerings = await Purchases.getOfferings();
Package? packageToPurchase;
// Find the package in current offering
if (offerings.current != null) {
for (var package in offerings.current!.availablePackages) {
if (package.storeProduct.identifier == productId) {
packageToPurchase = package;
break;
}
}
}
// If not found in current, search all offerings
if (packageToPurchase == null) {
for (var offering in offerings.all.values) {
for (var package in offering.availablePackages) {
if (package.storeProduct.identifier == productId) {
packageToPurchase = package;
break;
}
}
if (packageToPurchase != null) break;
}
}
if (packageToPurchase == null) {
log('Product not found in any offering: $productId');
return HeliumTransactionStatus.failed;
}
final purchaseResult = await Purchases.purchasePackage(packageToPurchase);
// Check if the purchase was successful by looking at entitlements
if (purchaseResult.customerInfo.entitlements.active.isNotEmpty) {
return HeliumTransactionStatus.purchased;
} else {
return HeliumTransactionStatus.failed;
}
} catch (e) {
log('RevenueCat purchase error: $e');
if (e is PurchasesErrorCode) {
if (e == PurchasesErrorCode.purchaseCancelledError) {
return HeliumTransactionStatus.cancelled;
} else if (e == PurchasesErrorCode.paymentPendingError) {
return HeliumTransactionStatus.pending;
}
}
return HeliumTransactionStatus.failed;
}
}
@override
Future<bool> restorePurchases(bool status) async {
try {
log('RevenueCat restoring purchases');
final restoredInfo = await Purchases.restorePurchases();
return restoredInfo.entitlements.active.isNotEmpty;
} catch (e) {
log('RevenueCat restore error: $e');
return false;
}
}
@override
Future<void> onPaywallEvent(Map<String, dynamic> heliumPaywallEvent) async {
log('RevenueCat paywall event: $heliumPaywallEvent');
// Handle specific events as needed
final eventType = heliumPaywallEvent['type'];
if (eventType == 'subscriptionSucceeded') {
// Handle successful subscription
final productId = heliumPaywallEvent['productKey'];
log('Subscription succeeded for product: $productId');
// Add your custom analytics tracking here
}
}
}
Initialize Helium and Download Paywall Configs #
In your app's initialization code (typically in main.dart
or your root widget), add the following to download paywall configurations:
import 'package:helium_flutter/helium_flutter.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Create your callbacks implementation
PaymentCallbacks paymentCallbacks = PaymentCallbacks();
// Initialize Helium
final heliumFlutter = HeliumFlutter();
heliumFlutter.initialize(
// You'll get this from Helium founders during setup!
apiKey: "<your-helium-api-key>",
// The callbacks implementation you created earlier
callbacks: paymentCallbacks,
// If set, a custom API endpoint (usually provided by Helium)
customAPIEndpoint: "https://api-v2.tryhelium.com/on-launch",
// If set, a custom user ID to use instead of Helium's
customUserId: "your-custom-user-id", // Optional
// Custom user traits for targeting and personalization
customUserTraits: {
"exampleUserTrait": "test_value",
"subscriptionStatus": "active",
"userIntent": "upgrade",
"numericalValue": 3.0,
}, // Optional
);
runApp(const MyApp());
}
Passing Custom User Traits
Custom user traits can be any key-value pairs where the value is a serializable type (String, num, bool, etc.). These traits can be used for targeting, personalization, and dynamic content in your paywalls.
Passing in a Custom User ID
By default, Helium generates a UUID per app session to identify users. You can override this with your own custom user ID (e.g., from a 3rd party analytics service) by passing it in the initialize
method or by explicitly calling overrideUserId
:
// Set a custom user ID
await heliumFlutter.overrideUserId(
newUserId: "your-custom-user-id",
traits: {
"exampleTrait": "value",
"userType": "premium"
}
);
Checking Download Status
After initialization, you can check the status of the paywall configuration download:
String downloadStatus = await heliumFlutter.getDownloadStatus() ?? 'Unknown';
The download status will be one of the following:
"notDownloadedYet"
: The download has not been initiated or is still in progress."downloadSuccess"
: The download was successful."downloadFailure"
: The download failed.
You can use this to handle different states in your app.
Checking if Paywalls are Loaded
You can also check if paywalls have been loaded successfully:
bool paywallsLoaded = await heliumFlutter.paywallsLoaded() ?? false;
Presenting Paywalls #
There are several ways to present Helium paywalls in your Flutter app:
Via Direct Method Call #
You can present a paywall programmatically using the presentUpsell
method:
ElevatedButton(
onPressed: () async {
await heliumFlutter.presentUpsell(trigger: 'onboarding');
},
child: Text('Show Premium Features'),
),
The trigger
parameter is a unique identifier for the paywall trigger point in your app. Helium uses this to track and optimize the paywall for each trigger point.
Via Widget Integration #
You can also use the UpsellViewForTrigger
widget to embed a paywall directly in your widget tree:
class ViewForTriggerPage extends StatelessWidget {
const ViewForTriggerPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: UpsellViewForTrigger(trigger: 'onboarding'),
);
}
}
Hiding Paywalls #
To programmatically hide a paywall:
bool hideResult = await heliumFlutter.hideUpsell() ?? false;
Handling Custom Dismissal Actions #
You can implement custom dismissal logic by handling paywall events in your HeliumCallbacks
implementation:
@override
Future<void> onPaywallEvent(Map<String, dynamic> heliumPaywallEvent) async {
final eventType = heliumPaywallEvent['type'];
if (eventType == 'ctaPressed') {
final ctaName = heliumPaywallEvent['ctaName'];
final triggerName = heliumPaywallEvent['triggerName'];
if (ctaName == 'dismiss') {
// Handle custom dismissal logic here
// For example, navigate back or show a different screen
}
}
}
Getting User ID #
To retrieve the Helium user ID:
String userId = await heliumFlutter.getHeliumUserId() ?? 'Unknown';
Testing #
Documentation for testing will be provided separately. After integration, please message us directly to get set up with a test app + in-app test support.