Skip to content
SDK

Displaying Flows

Control how and when flows appear in your app.

Displaying Flows

Flows can appear in your app automatically based on trigger conditions, or you can show them manually by ID. This page explains both approaches and the lifecycle callbacks you can listen for.

Automatic display

By default, the SDK forwards user signals to the Setgreet API whenever the user state changes -- after an identifyUser call, an event track, or a screen track. The server evaluates trigger conditions for the current user and, if a flow matches, returns it in the response. The SDK then displays the returned flow automatically.

This is the recommended approach for most use cases. You configure targeting in the dashboard, and the SDK handles the rest.

How trigger evaluation works

  1. The SDK sends a signal (identify, track event, or screen view) to the Setgreet API.
  2. The server checks all published flows to see if any trigger conditions match the current user state.
  3. If a match is found, the server returns the flow in its response.
  4. If multiple flows match, the server returns the one with the highest priority (set in the dashboard).
  5. The SDK renders the returned flow on the device.

Trigger evaluation is server-side, so the SDK requires a network connection to show flows. Offline caching is not supported today.

Manual display

If you want to show a specific flow at a specific moment in your app's code, use the showFlow method with the flow's ID:

// iOS
Setgreet.shared.showFlow(flowId: "YOUR_FLOW_ID")
// Android
Setgreet.showFlow("YOUR_FLOW_ID")
// React Native
import { showFlow } from '@setgreet/react-native-sdk';

showFlow('YOUR_FLOW_ID');
// Flutter
await Setgreet.showFlow("YOUR_FLOW_ID");

Manual display ignores the flow's trigger conditions -- the flow is shown immediately regardless of user attributes, events, or segments.

You can find the flow ID in the dashboard by opening a flow: it is the last part of the URL, app.setgreet.com/flows/<flow-id>. Flow IDs are 24-character hexadecimal strings.

Flow callbacks

Register callbacks to listen for flow lifecycle events. This lets your app respond to what happens during a flow -- for example, navigating to a screen after the flow closes, or logging a custom event.

// iOS
Setgreet.shared.setFlowCallbacks { callbacks in
    callbacks
        .onFlowStarted { event in
            print("Flow started: \(event.flowId)")
        }
        .onFlowCompleted { event in
            print("Flow completed: \(event.flowId)")
        }
        .onFlowDismissed { event in
            print("Flow dismissed: \(event.flowId), reason: \(event.reason)")
        }
        .onActionTriggered { event in
            print("Action triggered on flow \(event.flowId)")
        }
}
// Android
Setgreet.setFlowCallbacks {
    onFlowStarted { event ->
        println("Flow started: ${event.flowId}")
    }
    onFlowCompleted { event ->
        println("Flow completed: ${event.flowId}")
    }
    onFlowDismissed { event ->
        println("Flow dismissed: ${event.flowId}, reason: ${event.reason}")
    }
    onActionTriggered { event ->
        println("Action triggered on flow ${event.flowId}")
    }
}

Available lifecycle callbacks

CallbackDescription
onFlowStartedThe flow began and the first screen appeared.
onFlowCompletedThe user reached the flow's final screen.
onFlowDismissedThe flow closed without being completed.
onScreenChangedThe user navigated to another screen within the flow.
onActionTriggeredA button or component action was triggered.
onPermissionRequestedA permission request action fired (notifications, location, camera, tracking, microphone, photo library).
onTrackEventAn in-flow Track Event action fired.
onCustomActionA custom action defined in the dashboard fired.
onErrorAn error occurred while loading or rendering the flow.

Dismiss reasons

When onFlowDismissed fires, the event includes a reason explaining why the flow closed:

ReasonDescription
userCloseThe user tapped the close button, or tapped the dimmed area outside a bottom sheet or popup.
userSkipThe user tapped a skip button.
backPressThe user pressed the hardware or system back button (Android).
swipeDownThe user swiped a bottom sheet flow down.
replacedThe flow was replaced by another flow with higher priority.
programmaticThe flow was closed by app code.
completedThe flow reached its end and closed naturally.

Action types

Buttons and interactive components can trigger these actions:

Action typeDescription
nextNavigate to the next screen in the flow.
previousNavigate to the previous screen.
skipSkip the current step and move on.
dismissClose the flow.
urlOpen an external URL in the system browser.
requestNotificationPermissionTrigger the notification permission prompt.
requestLocationPermissionTrigger the location permission prompt.
requestCameraPermissionTrigger the camera permission prompt.
requestTrackingPermissionTrigger the App Tracking Transparency prompt (iOS only).
requestMicrophonePermissionTrigger the microphone permission prompt.
requestPhotoLibraryPermissionTrigger the photo library permission prompt.
requestReviewPresent the native "rate this app" prompt.
shareOpen the native share sheet.
openSettingsOpen the device settings page for the app.
trackEventFire an event through the SDK's tracking pipeline.
customA custom action handled by your app via onCustomAction.

Use these action callbacks to integrate flow outcomes with your app's navigation, state management, or business logic.

Permission actions

The SDK declares no permissions of its own. A permission prompt appears only when a flow uses one of the request actions and your app has made the platform declaration below. When the declaration is missing, the SDK logs a message, reports the permission as not required, and the flow moves on. It never crashes.

ActioniOS Info.plist keyAndroid <uses-permission>
requestNotificationPermissionnonePOST_NOTIFICATIONS (Android 13 and later; granted implicitly before)
requestLocationPermissionNSLocationWhenInUseUsageDescriptionACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
requestCameraPermissionNSCameraUsageDescriptionCAMERA
requestTrackingPermissionNSUserTrackingUsageDescriptionnone; Android has no equivalent, so the result is not required and the flow advances
requestMicrophonePermissionNSMicrophoneUsageDescriptionRECORD_AUDIO
requestPhotoLibraryPermissionNSPhotoLibraryUsageDescriptionREAD_MEDIA_IMAGES (Android 13 and later) or READ_EXTERNAL_STORAGE before

Every request fires onPermissionRequested with the permission type and a result: granted, denied, permanentlyDenied, alreadyGranted, or notRequired. On iOS, limited photo library access counts as granted. The flow advances after any result; a primer screen's job is to explain the request, not to block on it.

Apple accepts a screen that explains why tracking is requested before the App Tracking Transparency prompt. Apple's App Store Review Guidelines (5.1.2) prohibit offering a reward for allowing tracking or locking features behind it. Keep the copy explanatory.

A screen can also skip itself when the permission it asks for is already granted. See Skip when already granted.

On this page