Isolates navigation from UI and Business logic with simple wizard like mechanism.
Clone or download
Latest commit 6bcaa17 Jan 7, 2019


Download Android Weekly Android Arsenal


Isolates navigation from UI and Business logic by providing simple navigation mechanism.

Encapsulates exact implementation. It means navigation can be organized across Activities, Fragments or Views.

Survives configuration changes, can save his state and restore after process kill.



implementation 'com.github.hakobast:flowzard:0.1'

Create flows.

object Flows {
	const val LOGIN = "login"

class MainFlow(flowManager: FlowManager) : Flow(flowManager) {

	override fun onMessage(code: String, message: Any) {
		super.onMessage(code, message)
		if(code == "main" && message == "login"){

class LoginFlow(flowManager: FlowManager) : Flow(flowManager) {

    override fun onCreate(savedInstance: DataBunch?, data: DataBunch?) {
        super.onCreate(savedInstance, data)
	override fun onMessage(code: String, message: Any) {
		super.onMessage(code, message)
		if(code == "sign-up"){
		        "complete" -> endFlow()
		        "have-account" -> router.navigateTo("login")
		}else if(code == "login" && message == "complete"){

Extend Flow manager

class DefaultFlowManager : FlowManager() {

	override fun createMainFlow(): Flow {
		return MainFlow(this)

	override fun createFlow(id: String): Flow {
		return when (id) {
			Flows.LOGIN -> LoginFlow(this)
			else -> throw RuntimeException("Cannot find flow for id=$id")

and provide it in Application class.

class App : Application(), FlowManagerProvider {
    private val flowManager = DefaultFlowManager() 
    override fun getProvideManager(): FlowManager {
        return flowManager

Create flow Activity.

class MainActivity : FlowActivity() {

    override val navigator: Navigator
        get() = object : SimpleFlowNavigator(this) {

            override fun getActivityIntent(id: String, data: Any?): Intent {
                return when (id) {
                    Flows.LOGIN -> Intent(activity,
                    else -> throw RuntimeException("Cannot find activity for id=$id")

    override fun onCreate(savedInstanceState: Bundle?) {

        loginButton.setOnClickListener {
            flow.sendMessage("main", "login")

By default library provides FlowActivity.kt to easily integrate flowzard via activities.

Flowzard functionality

Flow provides navigation(with results) across flows.

protected fun newFlow(id: String, requestCode: Int? = null, data: Any? = null) // creates new flow
protected fun endFlow(result: Result? = null) // finishes current flow
protected open fun onFlowResult(requestCode: Int, result: Result) // receives result from flow 

Flow also lets screens to send/receive messages to/from flow.

protected fun sendMessageFromFlow(code: String, message: Any) // send message to screen.
protected open fun onMessage(code: String, message: Any) // receives result from screen.
fun sendMessage(code: String, message: Any) // send message from screen.
fun setMessageListener(code: String, listener: MessageHandler) // register to messages in screen.
fun removeMessageListener(code: String) // unregister from messages in screen.

Navigation in flow between screens via Router using kotlin version of Cicerone.

fun navigateTo(screenKey: String, data: Any? = null) // adds new screen to backstack
fun replace(screenKey: String, data: Any? = null) // replaces current screen with new one
fun add(screenKey: String, data: Any? = null) // adds new screen
fun setTo(screenKey: String, data: Any? = null) // clears backstack and adds new screen to it
fun exit() // clears backstack
fun backTo(screenKey: String) // pops backstack until screen.
fun back() // pops backstack

Surviving configuration changes and process death

The Api built having in mind configuration change and process death problems. Flows survive configuration changes and restores its state when process dies.

Sample projects



  • Special thanks to guys who created Cicerone for big inspiration.