ReusableComposeNode

Composable Function

Common
@Composable
public inline fun <T : Any, reified E : Applier<*>> ReusableComposeNode(
    noinline factory: () -> T,
    update: @DisallowComposableCalls Updater<T>.() -> Unit,
)

Emits a recyclable node into the composition of type T.

This function will throw a runtime exception if E is not a subtype of the applier of the currentComposer.

Parameters

factoryA function which will create a new instance of T. This function is NOT guaranteed to be called in place.
updateA function to perform updates on the node. This will run every time emit is executed. This function is called in place and will be inlined.
Common
@Composable
public inline fun <T : Any?, reified E : Applier<*>> ReusableComposeNode(
    noinline factory: () -> T,
    update: @DisallowComposableCalls Updater<T>.() -> Unit,
    content: @Composable () -> Unit,
)

Emits a recyclable node into the composition of type T. Nodes emitted inside of content will become children of the emitted node.

This function will throw a runtime exception if E is not a subtype of the applier of the currentComposer.

Parameters

factoryA function which will create a new instance of T. This function is NOT guaranteed to be called in place.
updateA function to perform updates on the node. This will run every time emit is executed. This function is called in place and will be inlined.
contentthe composable content that will emit the "children" of this node.
Common
@Composable
@ExplicitGroupsComposable
public inline fun <T, reified E : Applier<*>> ReusableComposeNode(
    noinline factory: () -> T,
    update: @DisallowComposableCalls Updater<T>.() -> Unit,
    noinline skippableUpdate: @Composable SkippableUpdater<T>.() -> Unit,
    content: @Composable () -> Unit,
)

Emits a recyclable node into the composition of type T. Nodes emitted inside of content will become children of the emitted node.

This function will throw a runtime exception if E is not a subtype of the applier of the currentComposer.

Parameters

factoryA function which will create a new instance of T. This function is NOT guaranteed to be called in place.
updateA function to perform updates on the node. This will run every time emit is executed. This function is called in place and will be inlined.
skippableUpdateA function to perform updates on the node. Unlike update, this function is Composable and will therefore be skipped unless it has been invalidated by some other mechanism. This can be useful to perform expensive calculations for updating the node where the calculations are likely to have the same inputs over time, so the function's execution can be skipped.
contentthe composable content that will emit the "children" of this node.

Code Examples

CustomTreeComposition

@Suppress("unused")
fun CustomTreeComposition() {
    // Provided we have a tree with a node base type like the following
    abstract class Node {
        val children = mutableListOf<Node>()
    }
    // We would implement an Applier class like the following, which would teach compose how to
    // manage a tree of Nodes.
    class NodeApplier(root: Node) : AbstractApplier<Node>(root) {
        override fun insertTopDown(index: Int, instance: Node) {
            current.children.add(index, instance)
        }
        override fun insertBottomUp(index: Int, instance: Node) {
            // Ignored as the tree is built top-down.
        }
        override fun remove(index: Int, count: Int) {
            current.children.remove(index, count)
        }
        override fun move(from: Int, to: Int, count: Int) {
            current.children.move(from, to, count)
        }
        override fun onClear() {
            root.children.clear()
        }
    }
    // A function like the following could be created to create a composition provided a root Node.
    fun Node.setContent(parent: CompositionContext, content: @Composable () -> Unit): Composition {
        return Composition(NodeApplier(this), parent).apply { setContent(content) }
    }
    // assuming we have Node sub-classes like "TextNode" and "GroupNode"
    class TextNode : Node() {
        var text: String = ""
        var onClick: () -> Unit = {}
    }
    class GroupNode : Node()
    // Composable equivalents could be created
    @Composable
    fun Text(text: String, onClick: () -> Unit = {}) {
        ComposeNode<TextNode, NodeApplier>(::TextNode) {
            set(text) { this.text = it }
            set(onClick) { this.onClick = it }
        }
    }
    @Composable
    fun Group(content: @Composable () -> Unit) {
        ComposeNode<GroupNode, NodeApplier>(::GroupNode, {}, content)
    }
    // and then a sample tree could be composed:
    fun runApp(root: GroupNode, parent: CompositionContext) {
        root.setContent(parent) {
            var count by remember { mutableStateOf(0) }
            Group {
                Text("Count: $count")
                Text("Increment") { count++ }
            }
        }
    }
}