ÿÿ Adapty Flow Builder クイックスタート | Adapty ドキュメント

Flutter SDK ã§ Flow Builder を使ã£ã¦è³¼å…¥ã‚’有効ã«ã™ã‚‹

アプリ内課金を有効ã«ã™ã‚‹ã«ã¯ã€æ¬¡ã®3ã¤ã®é‡è¦ãªæ¦‚念をç†è§£ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

  • プロダクト – ユーザーãŒè³¼å…¥ã§ãã‚‹ã‚‚ã®ï¼ˆã‚µãƒ–ã‚¹ã‚¯ãƒªãƒ—ã‚·ãƒ§ãƒ³ã€æ¶ˆè€—åž‹ã‚¢ã‚¤ãƒ†ãƒ ã€æ°¸ç¶šã‚¢ã‚¯ã‚»ã‚¹ï¼‰
  • フロー – ノーコードã®ãƒ•ロービルダーã§ä½œæˆã—ãŸã€ãƒ—ãƒ­ãƒ€ã‚¯ãƒˆã‚’ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«æç¤ºã™ã‚‹ãŸã‚ã®ç”»é¢ã‚·ãƒ¼ã‚±ãƒ³ã‚¹ã€‚SDK ã§ã¯ getFlow ã§å–å¾—ã—ã¾ã™ã€‚自å‰ã®ã‚³ãƒ¼ãƒ‰ã§ UI を構築ã—ãŸã„å ´åˆã¯ã€ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’ã”利用ãã ã•ㄠ— ペイウォールを手動ã§å®Ÿè£…ã™ã‚‹ã‚’ã”覧ãã ã•ã„。
  • プレースメント – アプリ内ã§ãƒ•ローを表示ã™ã‚‹å ´æ‰€ã¨ã‚¿ã‚¤ãƒŸãƒ³ã‚°ï¼ˆmainã€onboardingã€settings ãªã©ï¼‰ã€‚ダッシュボードã§ãƒ•ローをプレースメントã«ç´ä»˜ã‘ã€ã‚³ãƒ¼ãƒ‰å†…ã§ã¯ãƒ—レースメント ID ã§å–å¾—ã—ã¾ã™ã€‚ã“れã«ã‚ˆã‚Šã€A/B テストã®å®Ÿæ–½ã‚„ユーザーã”ã¨ã«ç•°ãªã‚‹ãƒ•ローã®è¡¨ç¤ºãŒç°¡å˜ã«è¡Œãˆã¾ã™ã€‚ Adaptyã§ã¯ã€ã‚¢ãƒ—リ内課金を有効ã«ã™ã‚‹æ–¹æ³•ã‚’3ã¤æä¾›ã—ã¦ã„ã¾ã™ã€‚アプリã®è¦ä»¶ã«å¿œã˜ã¦ã„ãšã‚Œã‹ã‚’é¸æŠžã—ã¦ãã ã•ã„: | 実装方法 | 複雑㕠| 使用タイミング | |------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Flow Builder | ✅ ç°¡å˜ | ノーコードビルダーã§è³¼å…¥å¯èƒ½ãªå®Œå…¨ãªãƒ•ローを作æˆã—ã¾ã™ã€‚Adapty ãŒè‡ªå‹•çš„ã«ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã—ã€è¤‡é›‘ãªè³¼å…¥ãƒ•ローã€ãƒ¬ã‚·ãƒ¼ãƒˆæ¤œè¨¼ã€ã‚µãƒ–スクリプション管ç†ã‚’ã™ã¹ã¦è£å´ã§å‡¦ç†ã—ã¾ã™ã€‚ | | 手動ã§ä½œæˆã—ãŸãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ« | 🟡 中程度 | アプリã®ã‚³ãƒ¼ãƒ‰ã§ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ« UI を実装ã—ã¤ã¤ã€ãƒ—ãƒ­ãƒ€ã‚¯ãƒˆãƒ©ã‚¤ãƒ³ãƒŠãƒƒãƒ—ã®æŸ”軟性を維æŒã™ã‚‹ãŸã‚ã« Adapty ã‹ã‚‰ãƒ•ローオブジェクトをå–å¾—ã—ã¾ã™ã€‚ガイドをå‚ç…§ã—ã¦ãã ã•ã„。 | | オブザーãƒãƒ¼ãƒ¢ãƒ¼ãƒ‰ | 🔴 難ã—ã„ | 独自ã®è³¼å…¥å‡¦ç†åŸºç›¤ãŒã™ã§ã«ã‚りã€ãã®ã¾ã¾ä½¿ã„ç¶šã‘ãŸã„å ´åˆã«ä½¿ç”¨ã—ã¾ã™ã€‚ãªãŠã€ã‚ªãƒ–ザーãƒãƒ¼ãƒ¢ãƒ¼ãƒ‰ã«ã¯ Adapty ã§ã®åˆ¶é™ãŒã‚りã¾ã™ã€‚記事をå‚ç…§ã—ã¦ãã ã•ã„。 |

ä»¥ä¸‹ã®æ‰‹é †ã§ã¯ã€Adapty Flow Builder ã§ä½œæˆã—ãŸãƒ•ローã®å®Ÿè£…方法を説明ã—ã¾ã™ã€‚

ペイウォールã®UIã‚’ç‹¬è‡ªã«æ§‹ç¯‰ã—ãŸã„å ´åˆã¯ã€ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’手動ã§å®Ÿè£…ã™ã‚‹ã‚’ã”覧ãã ã•ã„。

Adapty Flow Builder ã§ä½œæˆã—ãŸãƒ•ローをアプリã«è¡¨ç¤ºã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®3ã¤ã ã‘行ãˆã°OKã§ã™ã€‚

  1. フローをå–å¾—ã™ã‚‹: Adapty ã‹ã‚‰ãƒ•ローをå–å¾—ã—ã¾ã™ã€‚
  2. 表示ã™ã‚‹ï¼ˆè³¼å…¥å‡¦ç†ã¯AdaptyãŒè‡ªå‹•ã§è¡Œã„ã¾ã™ï¼‰: アプリã«ãƒ“ューを表示ã—ã¾ã™ã€‚
  3. ボタンæ“作を処ç†ã™ã‚‹: ユーザーã®ã‚¿ãƒƒãƒ—æ“作ã«å¯¾ã™ã‚‹ã‚¢ãƒ—リã®å応を定義ã—ã¾ã™ã€‚ãŸã¨ãˆã°ã€ãƒªãƒ³ã‚¯ã‚’é–‹ã„ãŸã‚Šã€ãƒœã‚¿ãƒ³ãŒã‚¿ãƒƒãƒ—ã•れãŸã¨ãã«ãƒ•ローを閉ã˜ãŸã‚Šã—ã¾ã™ã€‚

å§‹ã‚ã‚‹å‰ã«

å§‹ã‚ã‚‹å‰ã«ã€ä»¥ä¸‹ã®æ‰‹é †ã‚’完了ã—ã¦ãã ã•ã„:

  1. Adapty ダッシュボードã§ã‚¢ãƒ—リを App Store ãŠã‚ˆã³/ã¾ãŸã¯ Google Play ã«æŽ¥ç¶šã™ã‚‹ã€‚
  2. Adapty ã§ãƒ—ロダクトを作æˆã™ã‚‹ã€‚
  3. フローを作æˆã—ã¦ãƒ—ロダクトを追加ã™ã‚‹ã€‚
  4. プレースメントを作æˆã—ã¦ãƒ•ローを追加ã™ã‚‹ã€‚
  5. アプリã®ã‚³ãƒ¼ãƒ‰ã« Adapty SDK をインストールã—ã¦æœ‰åŠ¹åŒ–ã™ã‚‹ã€‚ã“ã®ã‚¬ã‚¤ãƒ‰ã§ã¯ Adapty Flutter SDK v4 ã® API を使用ã—ã¾ã™ã€‚

ã“れらã®ã‚¹ãƒ†ãƒƒãƒ—を最も素早ã完了ã™ã‚‹ã«ã¯ã€ã‚¯ã‚¤ãƒƒã‚¯ã‚¹ã‚¿ãƒ¼ãƒˆã‚¬ã‚¤ãƒ‰ã«å¾“ã†ã‹ã€Developer CLIを使用ã—ã¦ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã¨ãƒ—レースメントを作æˆã—ã¦ãã ã•ã„。

1. フローをå–å¾—ã™ã‚‹

フローã¯ãƒ€ãƒƒã‚·ãƒ¥ãƒœãƒ¼ãƒ‰ã§è¨­å®šã—ãŸãƒ—レースメントã«ç´ä»˜ã‘られã¦ã„ã¾ã™ã€‚プレースメントを使ã†ã¨ã€ã‚ªãƒ¼ãƒ‡ã‚£ã‚¨ãƒ³ã‚¹ã”ã¨ã«ç•°ãªã‚‹ãƒ•ローを表示ã—ãŸã‚Šã€A/B テストを実施ã—ãŸã‚Šã§ãã¾ã™ã€‚

Adapty フロービルダーã§ä½œæˆã—ãŸãƒ•ローをå–å¾—ã™ã‚‹ã«ã¯ã€æ¬¡ã®æ‰‹é †ã‚’行ã„ã¾ã™ã€‚

  1. getFlow メソッドを使ã£ã¦ãƒ—レースメント ID ã‹ã‚‰ flow オブジェクトをå–å¾—ã—ã€hasViewConfiguration プロパティを確èªã—ã¦ãƒ“ルダーã§ä½œæˆã•れãŸãƒ•ローã‹ã©ã†ã‹ã‚’判別ã—ã¾ã™ã€‚

  2. createFlowView メソッドを使ã£ã¦ãƒ•ロービューを作æˆã—ã¾ã™ã€‚ビューã«ã¯ãƒ•ローを表示ã™ã‚‹ãŸã‚ã«å¿…è¦ãª UI è¦ç´ ã¨ã‚¹ã‚¿ã‚¤ãƒ«æƒ…å ±ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚

ビュー設定をå–å¾—ã™ã‚‹ã«ã¯ã€ãƒ“ルダー㧠Show on device トグルをオンã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚オンã«ã—ã¦ã„ãªã„å ´åˆã€ãƒ“ュー設定ãŒç©ºã«ãªã‚Šã€ãƒ•ローãŒè¡¨ç¤ºã•れã¾ã›ã‚“。


try {
  // the requested flow
  final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
  final view = await AdaptyUI().createFlowView(
    flow: flow,
  );
} on AdaptyError catch (adaptyError) {
  // handle the error
} catch (e) {
  // handle the error
}

2. フローを表示ã™ã‚‹

フローã®ãƒ“ューãŒç”¨æ„ã§ããŸã‚‰ã€ã‚ã¨ã¯æ•°è¡Œè¿½åŠ ã™ã‚‹ã ã‘ã§è¡¨ç¤ºã§ãã¾ã™ã€‚

フローを表示ã™ã‚‹ã«ã¯ã€createFlowView メソッドã§ä½œæˆã—㟠view ã«å¯¾ã—㦠view.present() メソッドを呼ã³å‡ºã—ã¾ã™ã€‚å„ view ã¯ä¸€åº¦ã—ã‹è¡¨ç¤ºã§ãã¾ã›ã‚“。閉ã˜ãŸå¾Œã¯ãƒ¡ãƒ¢ãƒªã‹ã‚‰è§£æ”¾ã•れã¾ã™ã€‚å†åº¦ãƒ•ローを表示ã™ã‚‹å¿…è¦ãŒã‚ã‚‹å ´åˆã¯ã€createFlowView ã‚’ã‚‚ã†ä¸€åº¦å‘¼ã³å‡ºã—ã¦æ–°ã—ã„ view インスタンスを作æˆã—ã¦ãã ã•ã„。

try {
  await view.present();
} on AdaptyError catch (e) {
  // handle the error
} catch (e) {
  // handle the error
}

フローã®è¡¨ç¤ºæ–¹æ³•ã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€ã‚¬ã‚¤ãƒ‰ã‚’å‚ç…§ã—ã¦ãã ã•ã„。

3. ボタンアクションã®å‡¦ç†

ユーザーãŒãƒ•ロー内ã®ãƒœã‚¿ãƒ³ã‚’クリックã™ã‚‹ã¨ã€Flutter SDK ã¯è³¼å…¥ãƒ»å¾©å…ƒãƒ»ç”»é¢ã‚’é–‰ã˜ã‚‹ãƒ»URL ã‚’é–‹ããªã©ã®æ“作を自動的ã«å‡¦ç†ã—ã¾ã™ã€‚ãŸã ã—ã€ã‚«ã‚¹ã‚¿ãƒ ã¾ãŸã¯äº‹å‰å®šç¾©ã•れ㟠ID ã‚’æŒã¤ãƒœã‚¿ãƒ³ã«ã¤ã„ã¦ã¯ã€ã‚³ãƒ¼ãƒ‰å´ã§ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’処ç†ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

フロー画é¢ä¸Šã®ãƒ—ロセスを制御・監視ã™ã‚‹ã«ã¯ã€AdaptyUIFlowsEventsObserver ã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’実装ã—ã€ç”»é¢ã‚’表示ã™ã‚‹å‰ã«ã‚ªãƒ–ザーãƒãƒ¼ã‚’設定ã—ã¦ãã ã•ã„。ユーザーãŒä½•らã‹ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行ã™ã‚‹ã¨ flowViewDidPerformAction ãŒå‘¼ã³å‡ºã•れるã®ã§ã€ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ ID ã«å¿œã˜ã¦ã‚¢ãƒ—リãŒé©åˆ‡ã«å¿œç­”ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ 3ã¤ã®ã‚ªãƒ–ザーãƒãƒ¼ãƒ¡ã‚½ãƒƒãƒ‰ãŒå¿…é ˆã§ã™ï¼šflowViewDidFinishPurchaseã€flowViewDidFinishRestoreã€flowViewDidReceiveError — ã“れらãŒãªã„ã¨ã‚¯ãƒ©ã‚¹ã¯ã‚³ãƒ³ãƒ‘イルエラーã«ãªã‚Šã¾ã™ã€‚

ボタンã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¨ã‚¤ãƒ™ãƒ³ãƒˆã®å‡¦ç†æ–¹æ³•ã«ã¤ã„ã¦ã¯ã€ã‚¬ã‚¤ãƒ‰ã‚’ã”覧ãã ã•ã„。

ウィジェットã§ã¯ãªãã€å°‚用ã®é•·å‘½ãªã‚ªãƒ–ジェクトã¨ã—ã¦ã‚ªãƒ–ザーãƒãƒ¼ã‚’実装ã—ã¦ãã ã•ã„。アプリ全体ã§1ã¤ã®ã‚°ãƒ­ãƒ¼ãƒãƒ«ãªã‚ªãƒ–ザーãƒãƒ¼ã‚¹ãƒ­ãƒƒãƒˆãŒå…±æœ‰ã•れるãŸã‚ã€State ã«ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ã¨ãã®ç”»é¢ãŒãƒªãƒ¼ã‚¯ã—(SDKãŒå¼·å‚ç…§ã‚’ä¿æŒã™ã‚‹ãŸã‚ï¼‰ã€æ¬¡ã®ç”»é¢ãŒç™»éŒ²ã•ã‚ŒãŸæ™‚点ã§ã‚µã‚¤ãƒ¬ãƒ³ãƒˆã«ä¸Šæ›¸ãã•れã¦ã—ã¾ã„ã¾ã™ã€‚extends を使ã†ã“ã¨ã§SDKã®ãƒ‡ãƒ•ォルト動作も継承ã•れるãŸã‚ã€å¿…é ˆã®3ã¤ã®ãƒ¡ã‚½ãƒƒãƒ‰ã«åŠ ãˆã€å¿…è¦ãªã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã ã‘をオーãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã™ã‚Œã°å分ã§ã™ã€‚

// A dedicated, long-lived handler for flow events.
// It does NOT live inside a Widget/State, so it never leaks and is never
// silently replaced when screensÿÿ are pushed or popped.
class FlowEventsHandler extends AdaptyUIFlowsEventsObserver {
  // A single, app-wide instance — same idiom as Adapty() and AdaptyUI().
  static final FlowEventsHandler _instance = FlowEventsHandler._();
  factory FlowEventsHandler() => _instance;
  FlowEventsHandler._();

  // This method is called when user performs an action on the flow UI.
  // Overriding it replaces the default behavior (dismiss on close, open URLs),
  // so keep those cases if you want to preserve it.
  @override
  void flowViewDidPerformAction(AdaptyUIFlowView view, AdaptyUIAction action) {
    switch (action) {
      case const CloseAction():
      case const AndroidSystemBackAction(): // close the flow on the Android back button
        view.dismiss();
        break;
      case OpenUrlAction(:final url, :final openIn):
        AdaptyUI().openUrl(url, openIn: openIn);
        break;
      default:
        break;
    }
  }

  // Required: decide what happens after a purchase finishes
  @override
  void flowViewDidFinishPurchase(AdaptyUIFlowView view,
      AdaptyPaywallProduct product, AdaptyPurchaseResult purchaseResult) {
    if (purchaseResult is! AdaptyPurchaseResultUserCancelled) {
      view.dismiss();
    }
  }

  // Required: dismiss the flow once a restore succeeds
  @override
  void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
    view.dismiss();
  }

  // Required: handle rendering and other view errors
  @override
  void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
    print('Flow error: $error');
    view.dismiss();
  }
}

ãƒãƒ³ãƒ‰ãƒ©ãƒ¼ã¯ã€ãƒ•ローãŒè¡¨ç¤ºã•れるå‰ã®ã‚¢ãƒ—リ起動時㫠一度ã ã‘ 登録ã—ã¦ãã ã•ã„:

AdaptyUI().setFlowsEventsObserver(FlowEventsHandler());

次ã®ã‚¹ãƒ†ãƒƒãƒ—

ã”質å•ã‚„ãŠå›°ã‚Šã®ã“ã¨ãŒã‚れã°ã€ã‚µãƒãƒ¼ãƒˆãƒ•ォーラムをã”覧ãã ã•ã„。よãã‚る質å•ã¸ã®å›žç­”を見ã¤ã‘ãŸã‚Šã€ã”自身ã®è³ªå•を投稿ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ãƒãƒ¼ãƒ ã¨ã‚³ãƒŸãƒ¥ãƒ‹ãƒ†ã‚£ãŒã‚µãƒãƒ¼ãƒˆã„ãŸã—ã¾ã™ï¼

ペイウォールã¯ã‚¢ãƒ—リã«è¡¨ç¤ºã™ã‚‹æº–å‚™ãŒæ•´ã„ã¾ã—ãŸã€‚App Store サンドボックスã¾ãŸã¯Google Play Storeã§ãƒ†ã‚¹ãƒˆè³¼å…¥ã‚’試ã—ã¦ã€ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‹ã‚‰ãƒ†ã‚¹ãƒˆè³¼å…¥ãŒå®Œäº†ã§ãã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。

次ã«ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒ¬ãƒ™ãƒ«ã‚’確èªã—ã¦ã€é©åˆ‡ãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã—ãŸã‚Šæœ‰æ–™æ©Ÿèƒ½ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’付与ã—ãŸã‚Šã§ãるよã†ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

完全ãªã‚³ãƒ¼ãƒ‰ä¾‹

以下ã«ã€ã“れらã®ã™ã¹ã¦ã®ã‚¹ãƒ†ãƒƒãƒ—をアプリã«çµ±åˆã—ãŸå ´åˆã®å®Œå…¨ãªã‚µãƒ³ãƒ—ルコードを示ã—ã¾ã™ã€‚


void main() {
  // Register a single, long-lived observer once, before any flow is shown.
  // It is intentionally a plain object (NOT a Widget/State): its lifetime is the
  // whole app, so it never leaks and is never silently replaced when screens are
  // pushed or popped.
  AdaptyUI().setFlowsEventsObserver(FlowEventsHandler());

  runApp(MaterialApp(home: FlowScreen()));
}

/// A dedicated handler for AdaptyUI flow events.
///
/// It `extends` [AdaptyUIFlowsEventsObserver] (rather than being implemented
/// by a `State`), which gives you two things for free:
///   * the SDK's sensible defaults for optional callbacks, so besides the three
///     required methods you only override what you actually care about;
///   * a lifecycle that is independent of the widget tree — there is no strong
///     reference back into a `Widget`, so nothing leaks and there is nothing to
///     unregister.
///
/// Every callback receives the [AdaptyUIFlowView] it relates to, so handling
/// flow actions never requires a `BuildContext` or widget state.
class FlowEventsHandler extends AdaptyUIFlowsEventsObserver {
  // A single, app-wide instance — same idiom as Adapty() and AdaptyUI().
  static final FlowEventsHandler _instance = FlowEventsHandler._();
  factory FlowEventsHandler() => _instance;
  FlowEventsHandler._();

  // Called when the user performs an action on the flow UI.
  @override
  void flowViewDidPerformAction(AdaptyUIFlowView view, AdaptyUIAction action) {
    switch (action) {
      case const CloseAction():
      case const AndroidSystemBackAction(): // close the flow on the Android back button
        view.dismiss();
        break;
      case OpenUrlAction(:final url, :final openIn):
        // Open the URL natively, honoring the dashboard browser setting.
        AdaptyUI().openUrl(url, openIn: openIn);
        break;
      default:
        break;
    }
  }

  // Required: decide what happens after a purchase finishes.
  @override
  void flowViewDidFinishPurchase(AdaptyUIFlowView view,
      AdaptyPaywallProduct product, AdaptyPurchaseResult purchaseResult) {
    if (purchaseResult is! AdaptyPurchaseResultUserCancelled) {
      view.dismiss();
    }
  }

  // Required: dismiss the flow once a restore succeeds.
  @override
  void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
    view.dismiss();
  }

  // Required: handle rendering and other view errors.
  @override
  void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
    print('Flow error: $error');
    view.dismiss();
  }
}

class FlowScreen extends StatefulWidget {
  const FlowScreen({super.key});

  @override
  State<FlowScreen> createState() => _FlowScreenState();
}

class _FlowScreenState extends State<FlowScreen> {
  @override
  void initState() {
    super.initState();
    _showFlowIfNeeded();
  }

  Future<void> _showFlowIfNeeded() async {
    try {
      final flow = await Adapty().getFlow(
        placementId: 'YOUR_PLACEMENT_ID',
      );

      if (!flow.hasViewConfiguration) return;

      final view = await AdaptyUI().createFlowView(flow: flow);

      await view.present();
    } catch (_) {
      // Handle any errors (network, SDK issues, etc.)
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Adapty Flow Example')),
      body: Center(
        // Add a button to re-trigger the flow for testing purposes.
        child: ElevatedButton(
          onPressed: _showFlowIfNeeded,
          child: const Text('Show Flow'),
        ),
      ),
    );
  }
}
ÿÿÿÿ