Skip to content


Keeping extra concerns out of Node

Nodes are meant to be simple structural elements, and should be kept lean.

To keep the framework agnostic of any specific approach / pattern you want to use, there aren't any fixed parts. Rather, the Node offers an extension point using Plugins in its constructor:

abstract class Node(
    nodeContext: NodeContext,
    val view: NodeView = EmptyNodeView,
    plugins: List<Plugin> = emptyList() // <--

So what is a Plugin?

A Plugin is an empty interface extended by many actual ones:

interface Plugin


interface NodeLifecycleAware : Plugin {
    fun onCreate(lifecycle: Lifecycle) {}

fun interface Destroyable : Plugin {
    fun destroy()

Component level plugins

Sometimes you need to grab a reference to the component as a whole, either as an interface, or its implementation, the Node.

This will come especially handy when working with workflows.

interface NodeAware : Plugin {
    val node: Node<*>

    fun init(node: Node<*>) {}

There are helper classes found in the library, so you don't have to implement the above interfaces, you can just use delegation:

class SomeClass(
    private val nodeAware: NodeAware = NodeAwareImpl()
) : NodeAware by nodeAware {

    fun foo() {
        // [node] is an automatically available property coming from the NodeAware interface
        // the reference is automatically set for you by the framework + the NodeAwareImpl class
        // so you can use it right away:

⚠️ Note: the reference to node is set by Node automatically, and isn't available immediately after constructing your object, but only after the construction of the Node itself.

In case if you need to control navigation behaviour, you can use these plugins:

interface UpNavigationHandler : Plugin {
    fun handleUpNavigation(): Boolean = false

interface BackPressHandler : Plugin {
    val onBackPressedCallback: OnBackPressedCallback? get() = null

UpNavigationHandler controls Node.navigateUp behaviour and allows to intercept its invocation.

BackPressHandler controls device back press behaviour via androidx.activity.OnBackPressedCallback. You can read more about it here.

⚠️ Note: OnBackPressedCallback are invoked in the following order: 1. From children to parents. Render order of children matters! The last rendered child will be the first to handle back press. 2. Direct order of plugins within a node. Plugins are invoked in order they appears in Node(plugins = ...) before the AppyxComponent.

Using Plugins

All plugins are designed to have empty {} default implementations (or other sensible defaults when a return value is defined), so it's convenient to implement them only if you need.

Don't forget to pass your Plugins to your Node:

internal class MyNode(
    // ...
    plugins: List<Plugins> = emptyList()
    // ...
) : Node<Nothing>(
    // ...
    plugins = plugins
    // ...

⚠️ Note: plugins is a List, as the order matters here. All Plugin instances are invoked in the order they appear in the list.