Skip to content

Write Your Own Flow

The compose version flows component is not only a component that handles the onboarding processes, but also a framework for custom flows.

Create Your Own Flow

To create your own flow, you need to define a class and extend it from BaseFlow. For example:

class DemoCustomFlow(context: Context) : BaseFlow(context, "DemoCustomFlow") { ... }

This defines a custom flow, DemoCustomFlow, with the name DemoCustomFlow.

Add Steps

In BaseFlow, you can define the steps of your flow in the initialize function. For example:

    override fun initialize() {
        val step1 = SingleStep(context, "step_1") {
            EulaScreen(
                eulaContentLocale = localeState.value,
                eulaScreenSettings = EulaScreenSettings(
                    eulaUrl = "file:///android_res/raw/custom_eula.html",
                    agreeButtonCaption = android.R.string.ok,
                    disagreeButtonCaption = android.R.string.cancel
                ),
                agreeViewClickListener = {
                    flowDone("step_1")
                },
                disagreeViewClickListener = {
                    terminateFlowWithMessage("Custom flow cancelled.")
                },
                webViewClient = object : WebViewClient() {
                    override fun shouldOverrideUrlLoading(
                        view: WebView?,
                        request: WebResourceRequest?
                    ): Boolean =
                        request?.url?.let { url ->
                            SDKCustomTabsLauncher.launchCustomTabs(context, url.toString())
                            true
                        } ?: false
                }
            )
        }

        addSingleStep(step1)
    }

You can add a single step with addSingleStep, or a nest flow with addNestedFlow, which takes another flow as the argument.

fun addSingleStep(step: SingleStep, secure: Boolean = false) { ... }
fun addSingleStep(
        route: String,
        secure: Boolean = false,
        content: @Composable (NavBackStackEntry) -> Unit
    ) { ... }
fun addNestedFlow(flow: BaseFlow) { ... }

SingleStep is also a BaseFlow. The flow name of a SingleStep flow is the same as the route name of the single step.

Start And Navigation

    open fun start(popRoute: String? = null) {
        steps.first().start(popRoute = popRoute)
    }

Use the start function to start the flow: the default implementation will navigate to the first step as defined in the initialize function. However, if the first step should be determined at runtime, for example, for the restore flow, if biometrics are supported, the first step should be the biometric unlock screen, otherwise, the sign-in screen, then the implementation of your start function may look like:

override fun start(popRoute: String?) {
    if(biometricSupported) {
        navigateTo("step_unlock_with_biometri", popRoute)
    } else {
        navigateTo("step_sign_in", popRoute)
    }
}

Use navigateTo to navigate among your steps, as shown in the above sample code. The first argument is the destination route name, the second argument is the route name that will be popped out after navigation.

You can also use the following code for navigation:

    getChildFlowByName(routeName).start(popRoute)

routeName can be either the single step name or a nest flow name. getChildFlowByName can find the single step flow or the nest child flow, then start it and pop out the popRoute after navigation. This has the same effect as the navigateTo function.

Finish Or Terminate

open fun flowDone(routeOrFlowName: String, popRoute: String? = null) { ... }
fun terminateFlow(
        resultCode: Int = Activity.RESULT_OK,
        exitApp: Boolean = false,
        data: Intent? = null
    ) { ... }
protected fun terminateFlowWithMessage(message: String) { ... }
open suspend fun onFlowFinish(cancel: Boolean = false) = Unit

flowDone is where your flow notifies the parent flow that it's done and the parent flow can navigate to the next step or finish the whole flow if no other steps are specified. Because the route name of a single step flow is the flow name, in most cases your code will only need to call this function with the route name and the base flow will execute the next step in your flow, or navigate to the next nested flow in the parent.

terminateFlow will terminate the current flow without confirmation, if exitApp is true, the whole app process will be terminated. This is mainly used when you click the Back button on the sign-in screen during the restore flow. terminateFlowWithMessage will terminate the current flow after confirmation.

onFlowFinish will be called when the flow is finished, either completed or canceled, based on the cancel argument value. This is where you can do clean-up if necessary.


Last update: February 20, 2023