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

# Presenting Offers

> Present Encore retention offers to users at key moments in your Flutter app.

## Overview

Encore allows you to present targeted offers to users in exchange for rewards (free trials, discounts, credits) at critical moments like cancellation flows or feature paywalls. The native SDK renders all offer UI natively (SwiftUI on iOS, Jetpack Compose on Android) — the Flutter layer triggers presentation and receives results.

***

## Present Offers

Presenting an Encore offer is a single call:

```dart theme={null}
ElevatedButton(
  onPressed: () {
    Encore.placement('cancellation_flow').show();
  },
  child: const Text('Cancel Subscription'),
)
```

<Info>
  Register handlers before presenting. See [Handle Offer Results](#handle-offer-results) below.
</Info>

***

## Handle Offer Results

<Tip>
  **Choosing your integration shape**: This page shows the handler pattern. If your call site has direct access to purchase logic, the async-result pattern is often cleaner. See [Integration Patterns](/concepts/integration-patterns) for the decision tree and [Flutter Integration Patterns](/publishers/flutter/guides/integration-patterns) for Dart examples.
</Tip>

Register handlers that tell the SDK how to complete purchases and what to do when a user dismisses. Register these once after `configure()`.

### Register `onPurchaseRequest`

Called when a user accepts an offer and a purchase is needed. Use this to trigger a purchase via your Flutter-side subscription manager (RevenueCat, `in_app_purchase`, etc.).

```dart theme={null}
Encore.shared.onPurchaseRequest((purchaseRequest) async {
  // Example using RevenueCat Flutter SDK
  final products = await Purchases.getProducts([purchaseRequest.productId]);
  if (products.isNotEmpty) {
    await Purchases.purchaseStoreProduct(products.first);
  }
});
```

<Tip>
  If you **do not** set `onPurchaseRequest`, the native SDK handles the purchase itself using StoreKit (iOS) or Play Billing (Android) and presents the native subscription modal automatically. This is useful for apps that don't use a third-party subscription manager.
</Tip>

### Register `onPassthrough`

Called when the user dismisses the offer or no offers are available. Use this to resume the user's original action (e.g., proceed with cancellation).

```dart theme={null}
Encore.shared.onPassthrough((placementId) {
  // Resume your original user flow
  navigator.proceedWithCancellation();
});
```

### `onPurchaseComplete` (Optional)

Only fires when no `onPurchaseRequest` handler is set and the native SDK handles the purchase itself. Use this to sync the transaction with subscription managers that don't auto-detect native purchases.

```dart theme={null}
Encore.shared.onPurchaseComplete((result, productId) {
  debugPrint('Native purchase completed: $productId');
});
```

### Summary

| Callback             | When it fires                                         | Required?   |
| -------------------- | ----------------------------------------------------- | ----------- |
| `onPurchaseRequest`  | User accepts an offer, purchase needed                | Recommended |
| `onPassthrough`      | User dismisses or no offers available                 | Yes         |
| `onPurchaseComplete` | Native purchase finishes (no `onPurchaseRequest` set) | Optional    |

<Info>
  `onPassthrough` should be registered before presenting any placements so users are never blocked from completing their intended action.
</Info>

***

## Using the Result

`placement().show()` returns an `EncorePresentationResult` that you can use for flow control:

```dart theme={null}
final result = await Encore.placement('cancel_flow').show();

switch (result) {
  case EncorePresentationResultGranted(:final offerId):
    debugPrint('Offer granted: $offerId');
  case EncorePresentationResultNotGranted(:final reason):
    debugPrint('Not granted: $reason');
}
```

***

## Full Example

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:encore_flutter/encore_flutter.dart';

class CancelScreen extends StatelessWidget {
  const CancelScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Account')),
      body: Center(
        child: FilledButton(
          onPressed: () async {
            final result = await Encore.shared
                .placement('cancellation_flow')
                .show();

            if (result is EncorePresentationResultGranted) {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('Offer applied!')),
              );
            }
          },
          child: const Text('Cancel Subscription'),
        ),
      ),
    );
  }
}
```

***

## Next Steps

* **[Add Subscription Product](./add-subscription-product)** — Create a subscription product and connect it to Encore
