Flutter SDK ile Flow Builder kullanarak satın almaları etkinleştirin

Uygulama içi satın almaları etkinleştirmek için üç temel kavramı anlamanız gerekir:

  • Ürünler – kullanıcıların satın alabileceği her şey (abonelikler, consumable’lar, süresiz erişim)
  • Flow’lar – ürünleri kullanıcılara sunan ekran dizileri; no-code Flow Builder ile oluşturulur. SDK bunları getFlow aracılığıyla alır. Arayüzü kendi kodunuzla oluşturmayı tercih ederseniz bunun yerine paywall kullanın — bkz. Paywall’ları manuel olarak uygulama.
  • Placement’lar – flow’ları uygulamanızda nerede ve ne zaman göstereceğiniz (örneğin main, onboarding, settings). Flow’ları kontrol panelindeki placement’lara bağlar, ardından kodunuzda placement ID’siyle talep edersiniz. Bu sayede A/B testi yürütmek ve farklı kullanıcılara farklı flow’lar göstermek kolaylaşır. Adapty, uygulamanızda satın almaları etkinleştirmek için size üç yol sunar. Uygulama gereksinimlerinize göre birini seçin: | Uygulama | Karmaşıklık | Ne zaman kullanılır | |------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Adapty Flow Builder | ✅ Kolay | No-code builder’da eksiksiz, satın almaya hazır bir flow oluşturursunuz. Adapty bunu otomatik olarak render eder; karmaşık satın alma akışı, fiş doğrulama ve abonelik yönetimini arka planda halleder. | | Elle oluşturulan paywalllar | 🟡 Orta | Paywall arayüzünü uygulama kodunuzda kendiniz implement edersiniz, ancak ürün tekliflerinde esnekliği korumak için yine de Adapty’den flow nesnesini alırsınız. Rehbere bakın. | | Observer mode | 🔴 Zor | Zaten kendi satın alma altyapınız var ve bunu kullanmaya devam etmek istiyorsunuz. Observer mode’un Adapty’de bazı kısıtlamaları olduğunu unutmayın. Makaleye bakın. |

Aşağıdaki adımlar, Adapty Flow Builder’da oluşturulmuş bir flow’un nasıl uygulanacağını göstermektedir.

Paywall arayüzünü kendiniz oluşturmayı tercih ediyorsanız, bkz. Paywallları manuel olarak uygulama.

Adapty Flow Builder’da oluşturulmuş bir flow’u görüntülemek için uygulama kodunuzda yalnızca şunları yapmanız gerekir:

  1. Flow’u alın: Adapty’den alın.
  2. Görüntüleyin ve satın almaları Adapty yönetsin: Görünümü uygulamanızda gösterin.
  3. Buton eylemlerini yönetin: Kullanıcı etkileşimlerini uygulamanızın bu etkileşimlere verdiği yanıtlarla ilişkilendirin. Örneğin, kullanıcılar butonlara tıkladığında bağlantı açın veya flow’u kapatın.

Başlamadan önce

Başlamadan önce şu adımları tamamlayın:

  1. Uygulamanızı Adapty Kontrol Paneli’nde App Store ve/veya Google Play ile bağlayın.
  2. Adapty’de ürünlerinizi oluşturun.
  3. Bir flow oluşturun ve ona ürün ekleyin.
  4. Bir placement oluşturun ve flow’u ekleyin.
  5. Uygulama kodunuza Adapty SDK’yı yükleyip etkinleştirin. Bu kılavuz Adapty Flutter SDK v4 API’larını kullanır.

Bu adımları tamamlamanın en hızlı yolu, hızlı başlangıç kılavuzunu takip etmek veya Developer CLI kullanarak paywall’lar ve placement’lar oluşturmaktır.

1. Flow’u alın

Flow’larınız, kontrol panelinde yapılandırılmış placement’larla ilişkilendirilir. Placement’lar, farklı kitleler için farklı flow’lar çalıştırmanıza veya A/B testleri yapmanıza olanak tanır.

Adapty Flow Builder’da oluşturulmuş bir flow’u almak için şunları yapmanız gerekir:

  1. getFlow metodunu kullanarak placement ID’sine göre flow nesnesini alın ve hasViewConfiguration özelliğini kontrol ederek bunun builder’da oluşturulup oluşturulmadığını doğrulayın.

  2. createFlowView metodunu kullanarak flow view’ı oluşturun. View, flow’u görüntülemek için gereken UI öğelerini ve stil bilgilerini içerir.

Görünüm yapılandırmasını almak için builder’da Show on device geçişini açmanız gerekir. Aksi takdirde boş bir görünüm yapılandırması alırsınız ve flow görüntülenmez.


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. Flow’u görüntüleme

Artık flow view’a sahip olduğunuzda, onu görüntülemek için birkaç satır kod eklemeniz yeterli.

Flow’u görüntülemek için createFlowView metoduyla oluşturulan view üzerinde view.present() metodunu kullanın. Her view yalnızca bir kez gösterilebilir: kapatıldıktan sonra view bellekten silinir. Flow’u tekrar görüntülemeniz gerekiyorsa yeni bir view örneği oluşturmak için createFlowView metodunu bir kez daha çağırın.

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

Bir flow’un nasıl görüntüleneceğine dair daha fazla ayrıntı için kılavuzumuza bakın.

3. Buton eylemlerini yönetin

Kullanıcılar flow içindeki butonlara tıkladığında Flutter SDK, satın alma işlemlerini, restorasyon işlemlerini, görünümü kapatmayı ve URL açmayı otomatik olarak yönetir. Ancak diğer butonların özel veya önceden tanımlanmış ID’leri vardır ve kodunuzda bu eylemleri yönetmeniz gerekir.

Flow ekranındaki süreçleri kontrol etmek veya izlemek için AdaptyUIFlowsEventsObserver metodlarını uygulayın ve herhangi bir ekran gösterilmeden önce observer’ı ayarlayın. Bir kullanıcı bir eylem gerçekleştirdiğinde flowViewDidPerformAction tetiklenir ve uygulamanızın eylem ID’sine göre yanıt vermesi gerekir. Üç observer yöntemi zorunludur: flowViewDidFinishPurchase, flowViewDidFinishRestore ve flowViewDidReceiveError — bunlar olmadan sınıfınız derlenmez.

Buton aksiyonlarını ve olayları nasıl yöneteceğinize dair kılavuzlarımızı okuyun.

Gözlemciyi, bir widget’a bağlamak yerine bağımsız ve uzun ömürlü bir nesne olarak uygulayın. Uygulama genelinde tek bir global gözlemci slotu paylaşıldığından, bunu bir State’e bağlamak ekranı sızdırır (SDK ona güçlü bir referans tutar) ve bir sonraki ekran kendini kaydettiğinde sessizce değiştirilir. extends kullanmak aynı zamanda SDK’nın varsayılan davranışını miras aldığından, üç zorunlu metodun yanı sıra yalnızca önem verdiğiniz callback’leri override etmeniz yeterlidir.

// 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();
  }
}

İşleyiciyi uygulama başlangıcında, herhangi bir flow gösterilmeden önce bir kez kaydedin:

AdaptyUI().setFlowsEventsObserver(FlowEventsHandler());

Sonraki adımlar

Sorularınız mı var ya da sorunlarla mı karşılaşıyorsunuz? Sık sorulan soruların yanıtlarını bulabileceğiniz veya kendi sorularınızı sorabileceğiniz destek forumumuza göz atın. Ekibimiz ve topluluğumuz yardımcı olmak için burada!

Paywall’ınız uygulamada görüntülenmeye hazır. Paywall üzerinden test satın alma işlemini tamamlayabildiğinizden emin olmak için satın almalarınızı App Store sandbox ortamında veya Google Play Store’da test edin.

Şimdi, doğru kullanıcılara paywall gösterdiğinizden veya ücretli özelliklere erişim verdiğinizden emin olmak için kullanıcıların access level’ını kontrol etmeniz gerekiyor.

Tam örnek

Tüm bu adımların uygulamanızda birlikte nasıl entegre edileceğini aşağıda görebilirsiniz.


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'),
        ),
      ),
    );
  }
}