> ## 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.

# Ways to Show a Paywall

> Advanced and alternative ways to present Helium paywalls beyond the standard present method.

## Overview

Most of the time you'll want to use `presentPaywall()` (or `presentUpsell()` depending on your SDK version) to show paywalls. This approach is covered in each platform's quickstart guide (see navigation menu on the left). But if you need more control over how and where paywalls appear in your app, there are a few other options.

## iOS

#### Embedded View

```swift theme={null}
HeliumPaywall(
    trigger: "onboarding"
) { paywallNotShownReason in
    Text("Paywall failed to show")
}
```

<Warning>
  The iOS SDK will attempt to handle dismissal for embedded views but may not be able to depending on where you place it. You can dismissal yourself with PaywallEventHandlers.
</Warning>

<Accordion title="Example with PaywallEventHandlers">
  ```swift theme={null}
  HeliumPaywall(
      trigger: "post_onboarding",
      eventHandlers: PaywallEventHandlers()
          .onOpen { event in
              print("open for trigger \(event.triggerName)")
          }
          .onClose { event in
              print("close for trigger \(event.triggerName)")
          }
          .onDismissed { event in
              // handle user dismissal here (i.e. navigate to another screen)
              print("dismiss for trigger \(event.triggerName)")
          }
          .onPurchaseSucceeded { event in
              print("purchase made for trigger \(event.triggerName)")
          }
          .onCustomPaywallAction { event in
              print("Custom action: \(event.actionName) with params: \(event.params)")
          }
  ) { paywallNotShownReason in
      Text("Paywall failed to show")
  }
  ```
</Accordion>

<Accordion title="Keeping customPaywallTraits current when hosting in UIKit">
  `customPaywallTraits` are captured when the paywall **displays** and then stay fixed for that
  presentation. In SwiftUI this is handled for you as long as the traits come from state that
  re-renders the view. This is the normal way you'd pass dynamic values into a view.

  When you host `HeliumPaywall` in UIKit with a `UIHostingController`, UIKit won't refresh the view
  for you. If you build the hosting controller ahead of time and update traits before showing it,
  drive the view from an `ObservableObject` so the latest traits reach the paywall before display:

  ```swift theme={null}
  final class PaywallTraitsStore: ObservableObject {
      @Published var traits: HeliumUserTraits?
  }

  struct PaywallHost: View {
      @ObservedObject var store: PaywallTraitsStore
      let trigger: String
      var body: some View {
          HeliumPaywall(
              trigger: trigger,
              config: PaywallPresentationConfig(customPaywallTraits: store.traits)
          ) { paywallNotShownReason in
              Text("Paywall failed to show")
          }
      }
  }

  // UIKit
  let store = PaywallTraitsStore()
  store.traits = initialTraits
  let vc = UIHostingController(rootView: PaywallHost(store: store, trigger: "onboarding"))

  // Later, before the paywall is displayed:
  store.traits = freshTraits // reaches the paywall via the refreshed config
  ```

  <Note>
    This applies before the paywall is on screen. Once displayed, traits are locked for that
    presentation. Updating the store afterward won't change an already-shown paywall.
  </Note>
</Accordion>

#### SwiftUI ViewModifier

Attach a paywall to any SwiftUI view using the **`.heliumPaywall`** view modifier:

```swift theme={null}
struct ContentView: View {
    @State var isPresented: Bool = false
    var body: some View {
        VStack {
            Button {
                isPresented = true
            } label: {
                Text("Show paywall")
            }
        }.heliumPaywall(
            isPresented: $isPresented,
            trigger: "post_onboarding") { paywallNotShownReason in
                Text("Paywall failed to show")
            }
    }
}
```

<Note>
  If you are using UIKit, you can use a UIHostingController.
</Note>

## Android

#### Embedded Paywall (Compose)

<Note>
  Make sure to add `com.tryhelium.paywall:compose-ui:4.0.0` to your dependencies.
</Note>

```kotlin theme={null}
HeliumPaywall(
    trigger = "onboarding",
    onPaywallNotShown = { reason ->
        Text("Paywall failed to show")
    }
)
```

## Flutter

#### Widget Integration

<Warning>
  Flutter embedded widget currently only works for iOS. Android support is in the works.
</Warning>

Embed a paywall directly in your widget tree using **`HeliumFlutter.getUpsellWidget`**:

```dart theme={null}
class ExamplePageWithEmbeddedPaywall extends StatelessWidget {
  const ExamplePageWithEmbeddedPaywall({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: HeliumPaywall(
        trigger: "insert-trigger-here",
        paywallNotShownBuilder: (context, reason) => Text("Paywall failed to show"),
      ),
    );
  }
}
```

<Warning>
  You will have to handle your own dismissal. You can do so by passing in PaywallEventHandlers and using the `onDismissed` handler.
</Warning>
