> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryhelium.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Quickstart (Flutter)

> Integrate Helium into your Flutter App

<Tip>
  To get a better understanding of how paywalls are served, visit [here](/guides/how-does-helium-work).
</Tip>

# Present a Paywall

### Install the SDK

<Info>
  The minimum version of Flutter supported by this SDK is **3.24.0**.
</Info>

To add the Helium package, run:

```bash theme={null}
flutter pub add helium_flutter
```

Or if you prefer, you can add the helium\_flutter package to your pubspec.yaml:

```yaml theme={null}
dependencies:
  helium_flutter: ^3.1.3
```

then run:

```bash theme={null}
flutter pub get
```

<Tip>
  **Recommended -**  Make sure that Swift Package Manager support is enabled:
</Tip>

```bash theme={null}
flutter upgrade
flutter config --enable-swift-package-manager
```

See this [Flutter documentation](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers) for more details about Swift Package Manager.

<Info>
  You can still use Cocoapods for your dependencies if preferred. If you need to disable Swift Package Manager dependencies after having enabled it, refer to that same [Flutter documentation](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers#how-to-turn-off-swift-package-manager).
</Info>

#### iOS Settings

Helium requires iOS 15+. If your app already has a minimum of iOS 15 or higher, you're all set. This can be specified in your `ios/Podfile` with:

```text theme={null}
platform :ios, '15.0'
```

If you still see errors related to minimum iOS version, consider updating to 15.0 or higher [directly in the Xcode project](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers#how-to-use-a-swift-package-manager-flutter-plugin-that-requires-a-higher-os-version).

### Initialize Helium

<Tip>
  Find your API key [here](https://app.tryhelium.com/profile). If you do not have a Helium account set up yet, you can still integrate but will not be able to show a real paywall.
</Tip>

In your app's initialization code (typically in `main.dart` or your root widget):

```dart theme={null}
import 'package:helium_flutter/helium_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final heliumFlutter = HeliumFlutter();
  await heliumFlutter.initialize(
    apiKey: "<your-helium-api-key>",
  );

  runApp(const MyApp());
}
```

<ResponseField name="initialize" type="method">
  <Expandable title="parameters">
    <ResponseField name="apiKey" type="string" required>
      You can create or retrieve your api key from [Account Settings](https://app.tryhelium.com/profile).
    </ResponseField>

    <ResponseField name="callbacks" type="HeliumCallbacks?">
      *(Optional)* Pass in a listener for paywall events. See the Helium Events section.
    </ResponseField>

    <ResponseField name="purchaseDelegate" type="HeliumPurchaseDelegate?">
      *(Optional)* Delegate for handling your own purchase logic. If not provided, Helium will handle purchase logic for you. See the RevenueCat and Custom Purchase Handling sections.
    </ResponseField>

    <ResponseField name="fallbackPaywall" type="Widget?">
      *(Optional)* A widget to display if paywall fails to display. See Fallback Paywalls section.
    </ResponseField>

    <ResponseField name="customUserId" type="String?">
      *(Optional)* A custom user id to use instead of Helium's. We'll use this id when forwarding to third party analytics services, so this can be used for attribution. See Identifying Users section.
    </ResponseField>

    <ResponseField name="customUserTraits" type="Map<String, dynamic>?">
      *(Optional)* Custom user traits to be used for targeting, personalization, and dynamic paywall content. See Identifying Users section.
    </ResponseField>

    <ResponseField name="revenueCatAppUserId" type="String?">
      *(Optional)* RevenueCat ONLY. Supply RevenueCat appUserID here (and initialize RevenueCat before Helium initialize). See RevenueCat section.
    </ResponseField>

    <ResponseField name="fallbackBundleAssetPath" type="String?">
      *(Optional)* Path to fallback bundle. See Fallback Paywalls section.
    </ResponseField>

    <ResponseField name="paywallLoadingConfig" type="HeliumPaywallLoadingConfig?">
      *(Optional)* Set loading budget in seconds and whether to show a loading state. See Fallback Paywalls section.
    </ResponseField>
  </Expandable>
</ResponseField>

### Show Your First Paywall 🎉

<Tip>
  Set up a trigger and workflow in the [dashboard](https://app.tryhelium.com/workflows) to show your desired paywall.
</Tip>

Call `presentUpsell` wherever you want to show a full-screen paywall:

```dart theme={null}
ElevatedButton(
  onPressed: () {
    final heliumFlutter = HeliumFlutter();
    heliumFlutter.presentUpsell(context: context, trigger: 'insert-trigger-here');
  },
  child: Text('Show Premium Features'),
),
```

<Warning>
  Do not call `presentUpsell` in `Widget build()` as this can have unpredictable behavior.
</Warning>

You should now be able to see Helium paywalls in your app! Well done! 🎉

<Info>
  Looking for alternative presentation methods? Check out the guide on [Ways to Show a Paywall](/guides/ways-to-show-paywall).
</Info>

# Recommended Setup

Here are some common additional steps that you may want to consider.

### Identifying Users

Identifying users is optional but can help with targeting and when forwarding events to external analytics platforms. If you are not sure, you probably do not need to identify your users.

<Tip>
  Identify users as early as you can to maximize consistency in metrics and targeting. Ideally in your `initialize` call!
</Tip>

<CodeGroup>
  ```dart Set during initialize theme={null}
  await heliumFlutter.initialize(
    apiKey: "<your-helium-api-key>",
    customUserId: "your-custom-user-id",
    customUserTraits: {
      "exampleTrait": "value",
      "userType": "premium",
    },
  );
  ```

  ```dart Update after initialize theme={null}
  await heliumFlutter.overrideUserId(
    newUserId: "your-custom-user-id",
    traits: {
      "exampleTrait": "value",
      "userType": "premium",
    },
  );
  ```
</CodeGroup>

### Helium Events

Helium dispatches various events during paywall presentation and purchase flow. You can optionally handle these events in your mobile app. You can also configure Helium to forward them to your [existing analytics provider](/guides/third-party-analytics).

#### PaywallEventHandlers

When displaying a paywall you can pass in event handlers to listen for select events:

```dart theme={null}
HeliumFlutter().presentUpsell(
  trigger: 'my_paywall',
  context: context,
  eventHandlers: PaywallEventHandlers(
    onOpen: (event) {
      log('${event.type} - trigger: ${event.triggerName}');
    },
    onClose: (event) {
      log('${event.type} - trigger: ${event.triggerName}');
    },
    onDismissed: (event) {
      log('${event.type} - trigger: ${event.triggerName}');
    },
    onPurchaseSucceeded: (event) {
      log('${event.type} - trigger: ${event.triggerName}');
    },
    onAnyEvent: (event) {
      // A handler for all paywall-related events.
      // Note that if you have other handlers (i.e. onOpen) set up,
      // both that handler AND this one will fire during paywall open.
    },
  ),
);
```

#### Global Event Listener

You can also listen for all Helium events globally by implementing `HeliumCallbacks` and passing it to `initialize`:

```dart theme={null}
abstract class HeliumCallbacks {
  Future<void> onPaywallEvent(HeliumPaywallEvent heliumPaywallEvent);
}
```

For example:

```dart theme={null}
class LogCallbacks implements HeliumCallbacks {
  @override
  Future<void> onPaywallEvent(HeliumPaywallEvent heliumPaywallEvent) async {
    log('onPaywallEvent: ${heliumPaywallEvent.type} - trigger: ${heliumPaywallEvent.triggerName}');
  }
}
```

Then pass it to initialize:

```dart theme={null}
await heliumFlutter.initialize(
  apiKey: "<your-helium-api-key>",
  callbacks: LogCallbacks(),
);
```

### Fallback Paywalls

It is **highly recommended** that you set up "fallbacks" to handle the rare case when a paywall fails to display. Please follow [this guide](/guides/fallback-bundle) to do so.

<Tip>
  Do this *after* you have a paywall created that you want to use in production.
</Tip>

### Checking Subscription Status & Entitlements

<Tip>
  Check entitlements before showing paywalls to avoid showing a paywall to a user who should not see it.
</Tip>

<CodeGroup>
  ```dart Use dontShowIfAlreadyEntitled with presentUpsell theme={null}
  heliumFlutter.presentUpsell(
    trigger: 'my_paywall',
    context: context,
    dontShowIfAlreadyEntitled: true
  );
  ```

  ```dart Check before showing paywall theme={null}
  final heliumFlutter = HeliumFlutter();
  final hasActiveSubscription = await heliumFlutter.hasAnyActiveSubscription();
  if (hasActiveSubscription) {
    featureGatedLogic(); // replace this with your logic
  } else {
    heliumFlutter.presentUpsell(
      trigger: 'my_paywall',
      context: context,
      eventHandlers: PaywallEventHandlers(
        onPurchaseSucceeded: (event) {
          featureGatedLogic(); // replace this with your logic
        },
      )
    );
  }
  ```
</CodeGroup>

<Accordion title="All Entitlement Helper Methods">
  ```dart theme={null}
  /// Checks if the user has any active subscription (including non-renewable)
  Future<bool> hasAnyActiveSubscription();

  /// Checks if the user has any entitlement
  Future<bool> hasAnyEntitlement();

  /// Checks if the user has an active entitlement for any product attached to
  /// the paywall that will show for the provided trigger.
  /// Returns `null` if not known (i.e. the paywall is not downloaded yet).
  Future<bool?> hasEntitlementForPaywall(String trigger);
  ```
</Accordion>

# Advanced

### RevenueCat

<Note>
  By default, Helium will handle purchases for you! This section is typically for users who already use RevenueCat in their app.
</Note>

Helium integrates seamlessly with RevenueCat so you can continue to let RevenueCat handle your purchases and entitlements.

#### Install HeliumRevenueCat

Add the **helium\_revenuecat** package alongside helium\_flutter:

```bash theme={null}
flutter pub add helium_revenuecat
```

#### Configure Helium to use RevenueCat for Purchases

Use the built-in `RevenueCatPurchaseDelegate`:

```dart theme={null}
import 'package:helium_revenuecat/helium_revenuecat.dart';

final heliumFlutter = HeliumFlutter();
await heliumFlutter.initialize(
  apiKey: "<your-helium-api-key>",
  purchaseDelegate: RevenueCatPurchaseDelegate(),
);
```

<Warning>
  Make sure to configure RevenueCat *before* initializing Helium.
</Warning>

#### RevenueCat appUserID

If you ever [change the appUserID](https://www.revenuecat.com/docs/customers/identifying-customers#logging-in-after-configuration) of a user, keep Helium in sync with:

```dart theme={null}
HeliumFlutter().setRevenueCatAppUserId(await Purchases.appUserID);
```

### Custom Purchase Handling

<Note>
  By default, Helium will handle purchases for you! This section is for those who want to implement custom purchase logic.
</Note>

Implement `HeliumPurchaseDelegate` and pass it to `initialize`:

```dart theme={null}
abstract class HeliumPurchaseDelegate {
  Future<HeliumPurchaseResult> makePurchase(String productId);

  Future<bool> restorePurchases();
}
```

### Additional Features

<Note>
  For the full public API and detailed parameter documentation, see the inline docstrings in the SDK source. Import Helium in your project and use your IDE's autocomplete or jump-to-definition to explore all available methods and types.
</Note>

<Accordion title="Checking Download Status">
  <Tip>
    In most cases there is no need to check download status. Helium will display a loading indication if a paywall is presented before download has completed.
  </Tip>

  You can listen to the status of the paywalls download via a stream:

  ```dart theme={null}
  HeliumFlutter.downloadStatus.listen((status) {
    print('Download status: ${status.name}');
  });
  ```

  The download status will be one of the following:

  * `notDownloadedYet`: The download has not been initiated.
  * `inProgress`: The download is in progress.
  * `downloadSuccess`: The download was successful.
  * `downloadFailure`: The download failed.

  You can also check if paywalls have been downloaded with `await heliumFlutter.paywallsLoaded()`
</Accordion>

<Accordion title="Hiding Paywalls Programmatically">
  You can programmatically hide paywalls using:

  ```dart theme={null}
  // Hide the current paywall
  await HeliumFlutter().hideUpsell();

  // Hide all currently displayed paywalls
  await HeliumFlutter().hideAllUpsells();
  ```
</Accordion>

<Accordion title="Reset Helium">
  Reset Helium entirely so you can call initialize again, for example after changing user traits that can affect the paywalls a user might see via targeting.

  ```dart theme={null}
  await HeliumFlutter().resetHelium();
  ```
</Accordion>
