Skip to content

Dart (Flutter)

flutter_orca gives you one API that works everywhere Flutter runs. On iOS/macOS/Android it drives native in-app purchases under the hood; on web/Windows/Linux it routes through Stripe or GoCardless checkout. You write the same code either way — the package figures out which path to take.

Terminal window
flutter pub add orca

Unlike the native SDKs, this one isn’t a global singleton — you create an instance and keep it around (a provider, a singleton service, whatever fits your app’s state management):

import 'package:orca/orca.dart';
final orca = Orca(
publicKey: 'YOUR_ORCA_PUBLIC_KEY',
environment: OrcaEnvironment.sandbox, // .production for release
);
await orca.identify('user@example.com');

This also opens a live event stream (see step 5) so your app hears about purchases made outside your current session — handy on desktop/web where a purchase can finish in another tab.

final products = await orca.queryProducts(ExternalStore.stripe);
for (final product in products) {
print('${product.name}: ${product.formattedPrice}');
}

On mobile and macOS, the ExternalStore argument is ignored — native IAP is used automatically. On web/Windows/Linux, it decides whether you’re pricing against Stripe or GoCardless.

final entitlements = await orca.listEntitlements();
final selected = entitlements.first;
await orca.purchase(
selected,
externalStore: ExternalStore.stripe,
redirectUrl: 'https://yourapp.com/success',
failureRedirectUrl: 'https://yourapp.com/failure',
);

redirectUrl / failureRedirectUrl only matter for the web/desktop checkout path — on mobile they’re ignored since the native purchase sheet doesn’t redirect anywhere.

Purchases aren’t always synchronous — the App Store, Play Store, and web checkout all confirm asynchronously. Listen to the event stream instead of assuming purchase() resolving means success:

orca.purchaseEvents.listen((event) {
switch (event.event) {
case PurchaseEventType.success:
// unlock the feature, refresh entitlements
case PurchaseEventType.canceled:
// user backed out, no action needed
case PurchaseEventType.failed:
// show an error, maybe suggest retry
case PurchaseEventType.error:
// something went wrong on our end
}
});
final active = await orca.getActiveEntitlements();
final isPro = active.any((e) => e.entitlementId == 'pro_tier');

Or, if you want product-shaped data (price, name) instead of raw entitlement records:

final activeProducts = await orca.activeProduct();

Play Store and App Store handle proration automatically once you set up an Offering in the dashboard. For Stripe/GoCardless, tell Orca explicitly what the customer is switching from:

await orca.purchase(
newEntitlement,
externalStore: ExternalStore.stripe,
redirectUrl: 'https://yourapp.com/success',
failureRedirectUrl: 'https://yourapp.com/failure',
proratedProduct: currentEntitlement.products.stripe,
prorationMode: ProrationMode.upgrade, // or .downgrade
);

For App Store/Play Store subscriptions, customers manage or cancel through the platform’s own settings, not your app. Link them there:

// Android
launchUrl(Uri.parse(orca.playStoreManagementUrl));
// iOS / macOS
launchUrl(Uri.parse(orca.appStoreManagementUrl));
orca.logout(); // clears identity, stops the event stream
// when the widget/service holding this Orca instance is disposed:
orca.dispose();
class PaywallScreen extends StatefulWidget {
@override
State<PaywallScreen> createState() => _PaywallScreenState();
}
class _PaywallScreenState extends State<PaywallScreen> {
List<OrcaEntitlement> _entitlements = [];
StreamSubscription? _sub;
@override
void initState() {
super.initState();
_sub = orca.purchaseEvents.listen((event) {
if (event.event == PurchaseEventType.success) {
Navigator.of(context).pop(); // close paywall on success
}
});
orca.listEntitlements().then((e) => setState(() => _entitlements = e));
}
@override
void dispose() {
_sub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView(
children: _entitlements.map((e) {
return ListTile(
title: Text(e.name),
onTap: () => orca.purchase(
e,
externalStore: ExternalStore.stripe,
redirectUrl: 'https://yourapp.com/success',
failureRedirectUrl: 'https://yourapp.com/failure',
),
);
}).toList(),
);
}
}

flutter_orca only handles the client side of a purchase. Verifying access on your own backend, cancelling subscriptions remotely, or handling webhooks needs a server SDK: