---
title: "Afficher les flows et paywalls - React Native"
description: "Présentez des flows et des paywalls aux utilisateurs de votre application React Native avec Adapty."
---

<MethodPromo method="getFlow" label="Display flows and paywalls" />

Si vous avez créé un flow ou un paywall dans le Flow Builder, vous n'avez pas besoin de vous soucier de son rendu dans le code de votre application mobile pour l'afficher à l'utilisateur. Un tel flow contient à la fois ce qui doit être affiché et comment cela doit être affiché.

Avant de commencer, assurez-vous que :

1. Vous avez [créé un flow ou un paywall](create-paywall).
2. Vous l'avez ajouté à un [placement](placements).
3. Vous avez [récupéré le flow et préparé la vue](react-native-get-pb-paywalls).

:::warning

Ce guide concerne **les flows et les paywalls Paywall Builder** uniquement, qui nécessitent le SDK v4.0 ou une version ultérieure. Le processus de présentation des flows diffère pour les paywalls Remote Config.

- Pour présenter des **paywalls Remote Config**, consultez [Afficher un paywall conçu avec Remote Config](present-remote-config-paywalls).

:::

Le SDK Adapty React Native propose deux façons de présenter les flows et les paywalls :

- **Composant React** : un composant intégré vous permet de l'incorporer dans l'architecture et le système de navigation de votre application.

- **Présentation modale**

## Composant React \{#react-component\}

Pour intégrer un flow dans votre arborescence de composants existante, utilisez le composant `AdaptyFlowView` directement dans votre hiérarchie de composants React Native. Le composant intégré vous permet de l'incorporer dans l'architecture et le système de navigation de votre application.

:::tip
Le composant `AdaptyFlowView` crée sa vue au moment du rendu, c'est-à-dire lorsque la configuration et les images sont chargées. Pour les précharger, appelez [`createFlowView`](react-native-get-pb-paywalls#fetch-the-view-configuration) pour le même flow plus tôt dans votre application. Le composant réutilise alors les données en cache et s'affiche sans attendre les téléchargements.
:::

```typescript showLineNumbers title="React Native (TSX)"

function MyFlow({ flow }) {
  const flowParams = useMemo(() => ({
    loadTimeoutMs: 3000,
    locale: 'en', // The localization to render the flow with
  }), []);

  const onCloseButtonPress = useCallback<FlowEventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<FlowEventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<FlowEventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<FlowEventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<FlowEventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<FlowEventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<FlowEventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<FlowEventHandlers['onRestoreFailed']>((error) => {}, []);
  const onAppeared = useCallback<FlowEventHandlers['onAppeared']>(() => {}, []);
  const onError = useCallback<FlowEventHandlers['onError']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<FlowEventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<FlowEventHandlers['onUrlPress']>((url) => {}, []);
  const onCustomAction = useCallback<FlowEventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<FlowEventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyFlowView
      flow={flow}
      params={flowParams}
      style={styles.flow}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onAppeared={onAppeared}
      onError={onError}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onCustomAction={onCustomAction}
      onUrlPress={onUrlPress}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}
```

## Présentation modale \{#modal-presentation\}

Pour afficher un flow en tant qu'écran autonome, utilisez la méthode `view.present()` sur la `view` créée par la méthode [`createFlowView`](react-native-get-pb-paywalls#fetch-the-view-configuration). Chaque `view` ne peut être utilisée qu'une seule fois. Si vous avez besoin d'afficher le flow à nouveau, appelez `createFlowView` une fois de plus pour créer une nouvelle instance de `view`.

:::warning
Réutiliser la même `view` sans la recréer est interdit. Cela entraînera une erreur `AdaptyUIError.viewAlreadyPresented`.
:::

```typescript showLineNumbers title="React Native (TSX)"

const view = await createFlowView(flow);

// Optional: handle flow events (close, purchase, restore, etc)
// view.setEventHandlers({ ... });

try {
  await view.present();
} catch (error) {
  // handle the error
}
```

:::important
Appeler `setEventHandlers` plusieurs fois écrasera les gestionnaires que vous fournissez, remplaçant à la fois les gestionnaires par défaut et ceux précédemment définis pour ces événements spécifiques.
:::

### Configurer le style de présentation iOS \{#configure-ios-presentation-style\}

Configurez comment le flow est présenté sur iOS en passant le paramètre `iosPresentationStyle` à la méthode `present()`. Ce paramètre accepte les valeurs `'full_screen'` (par défaut) ou `'page_sheet'`.

```typescript showLineNumbers
try {
  await view.present({ iosPresentationStyle: 'page_sheet' });
} catch (error) {
  // handle the error
}
```

## Utiliser un minuteur défini par le développeur \{#use-developer-defined-timer\}

Pour utiliser des minuteurs définis par le développeur dans votre application mobile, utilisez le `timerId`, dans cet exemple `CUSTOM_TIMER_NY`, le **Timer ID** du minuteur défini par le développeur que vous avez configuré dans l'Adapty Dashboard. Cela garantit que votre application met à jour dynamiquement le minuteur avec la valeur correcte — comme `13d 09h 03m 34s` (calculée comme l'heure de fin du minuteur, par exemple le Jour de l'An, moins l'heure actuelle).

<Tabs>
<TabItem value="component" label="React component">
```typescript showLineNumbers title="React Native (TSX)"
const flowParams = {
  customTimers: { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }
};

<AdaptyFlowView
  flow={flow}
  params={flowParams}
  // ... your event handlers
/>
```
</TabItem>
<TabItem value="modal" label="Modal presentation">
```typescript showLineNumbers title="React Native (TSX)"
const customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) };

const view = await createFlowView(flow, { customTimers });
```
</TabItem>
</Tabs>
Dans cet exemple, `CUSTOM_TIMER_NY` est le **Timer ID** du minuteur défini par le développeur dans l'Adapty Dashboard. Le `timerResolver` permet à votre application de mettre à jour dynamiquement le minuteur avec la valeur correcte — par exemple `13j 09h 03m 34s` (calculée comme la date de fin du minuteur, comme le Jour de l'An, moins l'heure actuelle).

## Afficher une boîte de dialogue \{#show-dialog\}

Utilisez cette méthode à la place des boîtes de dialogue d'alerte natives lorsqu'une vue de flow est affichée sur Android. Sur Android, les alertes RN classiques apparaissent derrière la vue de flow, ce qui les rend invisibles pour les utilisateurs. Cette méthode garantit un affichage correct de la boîte de dialogue au-dessus du flow sur toutes les plateformes.

```typescript showLineNumbers title="React Native (TSX)"
try {
  const action = await view.showDialog({
    title: 'Close paywall?',
    content: 'You will lose access to exclusive offers.',
    primaryActionTitle: 'Stay',
    secondaryActionTitle: 'Close',
  });

  if (action === 'secondary') {
    // User confirmed - close the flow
    await view.dismiss();
  }
  // If primary - do nothing, user stays
} catch (error) {
  // handle error
}
```

## Remplacer un abonnement par un autre \{#replace-one-subscription-with-another\}

Lorsqu'un utilisateur tente d'acheter un nouvel abonnement alors qu'un autre est déjà actif sur Android, vous pouvez contrôler la façon dont ce nouvel achat doit être traité en passant des paramètres de mise à jour d'abonnement lors de la création de la vue flow. Pour remplacer l'abonnement actuel par le nouveau, utilisez `productPurchaseParams` dans `createFlowView` avec les paramètres `oldSubVendorProductId` et `prorationMode`.

```typescript showLineNumbers title="React Native (TSX)"

const productPurchaseParams = flow.paywalls
  .flatMap((variation) => variation.productIdentifiers)
  .map((productId) => {
    let params = {};
    if (Platform.OS === 'android') {
      params.android = {
        subscriptionUpdateParams: {
          oldSubVendorProductId: 'PRODUCT_ID_OF_THE_CURRENT_ACTIVE_SUBSCRIPTION',
          prorationMode: 'with_time_proration',
        },
      };
    }
    return { productId, params };
  });

const view = await createFlowView(flow, { productPurchaseParams });
```

---

> [!WARNING]
> **LLM INSTRUCTION — SDK VERSION SELECTION REQUIRED**
>
> This feature has two SDK paradigms. Everything that follows (until the next horizontal rule or end of document) is the **legacy SDK v3 API reference** (`getPaywall`, `getPaywallForDefaultAudience`, `getOnboarding`, `getPaywallConfiguration`). The **SDK v4+ `getFlow` method is documented above.** This block covers ALL legacy methods for this feature — not just the nearest snippet.
>
> **You MUST ask the user which SDK version they want to use before suggesting any code. Do not assume or default to a version.** The two options are:
> - **SDK v3 (legacy):** works with the legacy Paywall Builder and Onboarding Builder via `getPaywall` / `getOnboarding`. Use the legacy code in this section.
> - **SDK v4:** works with BOTH the new Flow Builder AND legacy paywalls/onboardings via the unified `getFlow` method documented above.
>
> Apply the legacy v3 code in this section ONLY when:
> 1. The user explicitly chose **SDK v3**, or
> 2. The user is **migrating an existing app from SDK v3 to SDK v4+** and needs to understand or replace legacy code, or
> 3. The user is **troubleshooting an already-deployed v3 integration**.
>
> **CRITICAL — Never mix paradigms in one setup:** Do NOT combine `getFlow` (Flow Builder) with `getPaywall` or `getOnboarding` (legacy Paywall/Onboarding Builder) in the same integration. These are incompatible patterns. Mixing them will produce inconsistent behavior and is unsupported.

Si vous avez personnalisé un paywall avec le Paywall Builder, vous n'avez pas à vous soucier de son rendu dans le code de votre application mobile pour l'afficher à l'utilisateur. Un tel paywall contient à la fois ce qui doit être affiché et comment cela doit l'être.

Avant de commencer, assurez-vous que :

1. Vous avez [créé un paywall](create-paywall).
2. Vous avez ajouté le paywall à un [placement](placements).
3. Vous avez [récupéré le paywall et préparé la vue](react-native-get-pb-paywalls).

:::warning

Ce guide concerne uniquement les **paywalls du nouveau Paywall Builder**, qui nécessitent le SDK v3.0 ou une version ultérieure. La procédure de présentation des paywalls diffère selon la version du Paywall Builder utilisée pour les concevoir et selon les paywalls avec Remote Config.

- Pour présenter des **paywalls avec Remote Config**, consultez [Afficher un paywall conçu avec Remote Config](present-remote-config-paywalls).

:::

Le SDK Adapty React Native propose deux façons de présenter les paywalls :

- **Composant React** : un composant intégré que vous pouvez incorporer à l'architecture et au système de navigation de votre application.

- **Présentation modale**

## Composant React \{#react-component\}

:::note
L'approche par **composant React** nécessite le SDK 3.14.0 ou une version ultérieure.
:::

Pour intégrer un paywall dans votre arborescence de composants existante, utilisez directement le composant `AdaptyPaywallView` dans la hiérarchie de composants React Native. Le composant intégré vous permet de l'incorporer dans l'architecture et le système de navigation de votre application.

:::note
Sur Android, si le paywall ne s'étend pas derrière la barre de statut, un overlay visuel peut apparaître en haut. Nous vous recommandons de le désactiver pour vos paywalls. Voir [Overlay visuel en haut du paywall (Android)](#visual-overlay-at-the-top-of-the-paywall-android).
:::

```typescript showLineNumbers title="React Native (TSX)"

function MyPaywall({ paywall }) {
  const paywallParams = useMemo(() => ({
    loadTimeoutMs: 3000,
  }), []);

  const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<EventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<EventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<EventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<EventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<EventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<EventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<EventHandlers['onRestoreFailed']>((error) => {}, []);
  const onPaywallShown = useCallback<EventHandlers['onPaywallShown']>(() => {}, []);
  const onRenderingFailed = useCallback<EventHandlers['onRenderingFailed']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<EventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => {}, []);
  const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<EventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyPaywallView
      paywall={paywall}
      params={paywallParams}
      style={styles.paywall}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onPaywallShown={onPaywallShown}
      onRenderingFailed={onRenderingFailed}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onCustomAction={onCustomAction}
      onUrlPress={onUrlPress}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}
```

## Présentation modale \{#modal-presentation\}

Pour afficher un paywall en tant qu'écran autonome, utilisez la méthode `view.present()` sur la `view` créée par la méthode [`createPaywallView`](react-native-get-pb-paywalls#fetch-the-view-configuration-of-paywall-designed-using-paywall-builder). Chaque `view` ne peut être utilisée qu'une seule fois. Si vous avez besoin d'afficher le paywall à nouveau, appelez `createPaywallView` une nouvelle fois pour créer une nouvelle instance de `view`.

:::warning
Réutiliser la même `view` sans la recréer est interdit. Cela provoquera une erreur `AdaptyUIError.viewAlreadyPresented`.
:::

```typescript showLineNumbers title="React Native (TSX)"

const view = await createPaywallView(paywall);

// Optional: handle paywall events (close, purchase, restore, etc)
// view.setEventHandlers({ ... });

try {
  await view.present();
} catch (error) {
  // handle the error
}
```

:::important
Appeler `setEventHandlers` plusieurs fois remplacera les gestionnaires que vous fournissez, en écrasant à la fois les gestionnaires par défaut et ceux précédemment définis pour ces événements spécifiques.
:::

### Configurer le style de présentation iOS \{#configure-ios-presentation-style\}

Configurez la façon dont le paywall est présenté sur iOS en passant le paramètre `iosPresentationStyle` à la méthode `present()`. Ce paramètre accepte les valeurs `'full_screen'` (par défaut) ou `'page_sheet'`.

```typescript showLineNumbers
try {
  await view.present({ iosPresentationStyle: 'page_sheet' });
} catch (error) {
  // handle the error
}
```

## Utiliser un minuteur défini par le développeur \{#use-developer-defined-timer\}

Pour utiliser des minuteurs définis par le développeur dans votre application mobile, utilisez le `timerId`, dans cet exemple `CUSTOM_TIMER_NY`, le **Timer ID** du minuteur défini par le développeur que vous avez configuré dans l'Adapty Dashboard. Cela permet à votre application de mettre à jour dynamiquement le minuteur avec la valeur correcte — par exemple `13d 09h 03m 34s` (calculée comme la date de fin du minuteur, par exemple le Jour de l'An, moins l'heure actuelle).

<Tabs>
<TabItem value="component" label="React component">
```typescript showLineNumbers title="React Native (TSX)"
const paywallParams = {
  customTimers: { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) }
};

<AdaptyPaywallView
  paywall={paywall}
  params={paywallParams}
  // ... your event handlers
/>
```
</TabItem>
<TabItem value="modal" label="Modal presentation">
```typescript showLineNumbers title="React Native (TSX)"
const customTimers = { 'CUSTOM_TIMER_NY': new Date(2025, 0, 1) };

const view = await createPaywallView(paywall, { customTimers });
```
</TabItem>
</Tabs>
Dans cet exemple, `CUSTOM_TIMER_NY` est le **Timer ID** du timer défini par le développeur que vous avez configuré dans l'Adapty Dashboard. Le `timerResolver` permet à votre application de mettre à jour dynamiquement le timer avec la valeur correcte — par exemple `13d 09h 03m 34s` (calculée comme la date de fin du timer, comme le Jour de l'An, moins l'heure actuelle).

## Afficher une boîte de dialogue \{#show-dialog\}

Utilisez cette méthode à la place des boîtes de dialogue d'alerte natives lorsqu'une vue de paywall est affichée sur Android. Sur Android, les alertes RN classiques apparaissent derrière la vue du paywall, ce qui les rend invisibles pour les utilisateurs. Cette méthode garantit un affichage correct de la boîte de dialogue au-dessus du paywall sur toutes les plateformes.

```typescript showLineNumbers title="React Native (TSX)"
try {
  const action = await view.showDialog({
    title: 'Close paywall?',
    content: 'You will lose access to exclusive offers.',
    primaryActionTitle: 'Stay',
    secondaryActionTitle: 'Close',
  });

  if (action === 'secondary') {
    // User confirmed - close the paywall
    await view.dismiss();
  }
  // If primary - do nothing, user stays
} catch (error) {
  // handle error
}
```

## Remplacer un abonnement par un autre \{#replace-one-subscription-with-another\}

Lorsqu'un utilisateur tente d'acheter un nouvel abonnement alors qu'un autre abonnement est actif sur Android, vous pouvez contrôler la façon dont le nouvel achat doit être traité en passant des paramètres de mise à jour d'abonnement lors de la création de la vue paywall. Pour remplacer l'abonnement actuel par le nouveau, utilisez `productPurchaseParams` dans `createPaywallView` avec les paramètres `oldSubVendorProductId` et `prorationMode`.

```typescript showLineNumbers title="React Native (TSX)"

const productPurchaseParams = paywall.productIdentifiers.map((productId) => {
  let params = {};
  if (Platform.OS === 'android') {
    params.android = {
      subscriptionUpdateParams: {
        oldSubVendorProductId: 'PRODUCT_ID_OF_THE_CURRENT_ACTIVE_SUBSCRIPTION',
        prorationMode: 'with_time_proration',
      },
    };
  }
  return { productId, params };
});

const view = await createPaywallView(paywall, { productPurchaseParams });
```

## Résolution des problèmes \{#troubleshooting\}

### Superposition visuelle en haut du paywall (Android) \{#visual-overlay-at-the-top-of-the-paywall-android\}

:::note
Ce paramètre est pris en charge à partir du SDK React Native 3.15.5 et n'est disponible que dans les projets React Native en mode bare.

Si vous utilisez un workflow géré par Expo, vous ne pouvez pas ajouter cette ressource Android directement. Pour appliquer ce paramètre, vous devez créer un plugin de configuration Expo personnalisé qui ajoute la ressource Android correspondante et l'enregistrer dans app.config.js. Cela est nécessaire car Expo gère le projet Android natif à votre place.
:::

Si `AdaptyPaywallView` ne s'étend pas derrière la barre de statut, une superposition visuelle peut quand même apparaître en haut. Pour la supprimer, ajoutez la ressource booléenne suivante à votre application :

1. Accédez à `android/app/src/main/res/values`. S'il n'existe pas de fichier `bools.xml`, créez-le.

2. Ajoutez la ressource suivante :

```xml
<resources>
    <bool name="adapty_paywall_enable_safe_area_paddings">false</bool>
</resources>
```

Notez que ces modifications s'appliquent globalement à tous les paywalls de votre application.

---