DelegatableNode

Known direct subclasses
CompositionLocalConsumerModifierNode

Implementing this interface allows your Modifier.Node subclass to read CompositionLocals via the currentValueOf function.

DragAndDropModifierNode

A Modifier.Node providing low level access to platform drag and drop operations.

DrawModifierNode

A Modifier.Node that draws into the space of the layout.

FocusEventModifierNode

Implement this interface create a modifier node that can be used to observe focus state changes to a FocusTargetNode down the hierarchy.

FocusPropertiesModifierNode

Implement this interface create a modifier node that can be used to modify the focus properties of the associated FocusTargetNode.

FocusRequesterModifierNode

Implement this interface to create a modifier node that can be used to request changes in the focus state of a FocusTargetNode down the hierarchy.

FocusTargetModifierNode

This modifier node can be used to create a modifier that makes a component focusable.

GlobalPositionAwareModifierNode

A androidx.compose.ui.Modifier.Node whose onGloballyPositioned is called with the final LayoutCoordinates of the Layout when the global position of the content may have changed.

KeyInputModifierNode

Implement this interface to create a Modifier.Node that can intercept hardware Key events.

LayoutAwareModifierNode

A androidx.compose.ui.Modifier.Node which receives various callbacks in response to local changes in layout.

LayoutModifierNode

A Modifier.Node that changes how its wrapped content is measured and laid out.

Modifier.Node

The longer-lived object that is created for each Modifier.Element applied to a androidx.compose.ui.layout.Layout.

ModifierLocalModifierNode

A androidx.compose.ui.Modifier.Node that is capable of consuming and providing ModifierLocal values.

ObserverModifierNode

Modifier.Nodes that implement ObserverNode can provide their own implementation of onObservedReadsChanged that will be called in response to changes to snapshot objects read within an observeReads block.

ParentDataModifierNode

A Modifier.Node that provides data to the parent Layout.

PlatformTextInputModifierNode

A modifier node that can connect to the platform's text input IME system.

PointerInputModifierNode

A androidx.compose.ui.Modifier.Node that receives PointerInputChanges, interprets them, and consumes the aspects of the changes that it is react to such that other PointerInputModifierNodes don't also react to them.

RotaryInputModifierNode

Implement this interface to create a Modifier.Node that can intercept rotary scroll events.

SemanticsModifierNode

A Modifier.Node that adds semantics key/value for use in testing, accessibility, and similar use cases.

SoftKeyboardInterceptionModifierNode

Implement this interface to create a Modifier.Node that can intercept hardware Key events before they are sent to the software keyboard.

TraversableNode

Allows Modifier.Node classes to traverse up/down the Node tree for classes of the same type or for a particular key (traverseKey).

Known indirect subclasses
ApproachLayoutModifierNode

ApproachLayoutModifierNode is designed to support gradually approaching the destination layout calculated in the lookahead pass.

CacheDrawModifierNode

Expands on the androidx.compose.ui.node.DrawModifierNode by adding the ability to invalidate the draw cache for changes in things like shapes and bitmaps (see Modifier.border for a usage examples).

CombinedClickableNode

Public interface for the internal node used inside combinedClickable, to allow for custom modifier nodes to delegate to it.

DelegatingNode

A Modifier.Node which is able to delegate work to other Modifier.Node instances.

SuspendingPointerInputModifierNode

Extends PointerInputModifierNode with a handler to execute asynchronously when an event occurs and a function to reset that handler (cancels the existing coroutine and essentially resets the handler's execution).


Represents a Modifier.Node which can be a delegate of another Modifier.Node. Since Modifier.Node implements this interface, in practice any Modifier.Node can be delegated.

Summary

Public properties

Modifier.Node

A reference of the Modifier.Node that holds this node's position in the node hierarchy.

Cmn

Extension functions

Unit

Invalidates the subtree of this layout, including layout, drawing, parent data, etc.

Cmn
Density

Returns the current Density of the LayoutNode that this DelegatableNode is attached to.

Cmn
LayoutCoordinates

Returns the LayoutCoordinates of this node.

Cmn
LayoutDirection

Returns the current LayoutDirection of the LayoutNode that this DelegatableNode is attached to.

Cmn
View

The Android View hosting the composition.

android
suspend Unit

Bring this node into bounds by making all the scrollable parents scroll appropriately.

Cmn
TraversableNode?

Finds the nearest traversable ancestor with a matching key.

Cmn
Unit
DelegatableNode.traverseAncestors(
    key: Any?,
    block: (TraversableNode) -> Boolean
)

Executes block for all ancestors with a matching key.

Cmn
Unit
DelegatableNode.traverseChildren(
    key: Any?,
    block: (TraversableNode) -> Boolean
)

Executes block for all direct children of the node with a matching key.

Cmn
Unit

Conditionally executes block for each descendant with a matching key.

Cmn

Public properties

node

val nodeModifier.Node

A reference of the Modifier.Node that holds this node's position in the node hierarchy. If the node is a delegate of another node, this will point to the root delegating node that is actually part of the node tree. Otherwise, this will point to itself.

Extension functions

invalidateSubtree

fun DelegatableNode.invalidateSubtree(): Unit

Invalidates the subtree of this layout, including layout, drawing, parent data, etc.

Calling this method can be a relatively expensive operation as it will cause the entire subtree to relayout and redraw instead of just parts that are otherwise invalidated. Its use should be limited to structural changes.

requireDensity

fun DelegatableNode.requireDensity(): Density

Returns the current Density of the LayoutNode that this DelegatableNode is attached to. If the node is not attached, this function will throw an IllegalStateException.

requireLayoutCoordinates

fun DelegatableNode.requireLayoutCoordinates(): LayoutCoordinates

Returns the LayoutCoordinates of this node.

To get a signal when the LayoutCoordinates become available, or when its parent places it, implement LayoutAwareModifierNode.

Throws
kotlin.IllegalStateException

When either this node is not attached, or the LayoutCoordinates object is not attached.

requireLayoutDirection

fun DelegatableNode.requireLayoutDirection(): LayoutDirection

Returns the current LayoutDirection of the LayoutNode that this DelegatableNode is attached to. If the node is not attached, this function will throw an IllegalStateException.

requireView

fun DelegatableNode.requireView(): View

The Android View hosting the composition.

Throws
kotlin.IllegalStateException

If the modifier node is not attached.

suspend fun DelegatableNode.scrollIntoView(rect: Rect? = null): Unit

Bring this node into bounds by making all the scrollable parents scroll appropriately.

This method will not return until this request is satisfied or a newer request interrupts it. If this call is interrupted by a newer call, this method will throw a CancellationException.

import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.focus.onFocusChanged

Row(Modifier.horizontalScroll(rememberScrollState())) {
    repeat(100) {
        val bringIntoViewRequester = remember { BringIntoViewRequester() }
        val coroutineScope = rememberCoroutineScope()
        Box(
            Modifier
                // This associates the RelocationRequester with a Composable that wants to be
                // brought into view.
                .bringIntoViewRequester(bringIntoViewRequester)
                .onFocusChanged {
                    if (it.isFocused) {
                        coroutineScope.launch {
                            // This sends a request to all parents that asks them to scroll so
                            // that this item is brought into view.
                            bringIntoViewRequester.bringIntoView()
                        }
                    }
                }
                .focusTarget()
        )
    }
}
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.border
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect

with(LocalDensity.current) {
    val bringIntoViewRequester = remember { BringIntoViewRequester() }
    val coroutineScope = rememberCoroutineScope()
    Column {
        Box(
            Modifier
                .border(2.dp, Color.Black)
                .size(500f.toDp())
                .horizontalScroll(rememberScrollState())
        ) {
            Canvas(
                Modifier
                    .size(1500f.toDp(), 500f.toDp())
                    // This associates the RelocationRequester with a Composable that wants
                    // to be brought into view.
                    .bringIntoViewRequester(bringIntoViewRequester)
            ) {
                drawCircle(color = Color.Red, radius = 250f, center = Offset(750f, 250f))
            }
        }
        Button(
            onClick = {
                val circleCoordinates = Rect(500f, 0f, 1000f, 500f)
                coroutineScope.launch {
                    // This sends a request to all parents that asks them to scroll so that
                    // the circle is brought into view.
                    bringIntoViewRequester.bringIntoView(circleCoordinates)
                }
            }
        ) {
            Text("Bring circle into View")
        }
    }
}
Parameters
rect: Rect? = null

The rectangle (In local coordinates) that should be brought into view. If you don't specify the coordinates, the coordinates of the Modifier.bringIntoViewRequester() associated with this BringIntoViewRequester will be used.

findNearestAncestor

fun DelegatableNode.findNearestAncestor(key: Any?): TraversableNode?

Finds the nearest traversable ancestor with a matching key.

traverseAncestors

fun DelegatableNode.traverseAncestors(
    key: Any?,
    block: (TraversableNode) -> Boolean
): Unit

Executes block for all ancestors with a matching key.

Note: The parameter block's return boolean value will determine if the traversal will continue (true = continue, false = cancel).

import androidx.compose.ui.node.traverseAncestors

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseAncestors(traverseKey) {
        if (it is CustomTraversableModifierNode) {
            it.doSomethingWithAncestor()
        }
        // Return true to continue searching the tree after a match. If you were looking to
        // match only some of the nodes, you could return false and stop executing the search.
        true
    }
}

traverseChildren

fun DelegatableNode.traverseChildren(
    key: Any?,
    block: (TraversableNode) -> Boolean
): Unit

Executes block for all direct children of the node with a matching key.

Note 1: This stops at the children and does not include grandchildren and so on down the tree.

Note 2: The parameter block's return boolean value will determine if the traversal will continue (true = continue, false = cancel).

import androidx.compose.ui.node.traverseChildren

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseChildren(traverseKey) {
        if (it is CustomTraversableModifierNode) {
            it.doSomethingWithChild()
        }
        // Return true to continue searching the tree after a match. If you were looking to
        // match only some of the nodes, you could return false and stop executing the search.
        true
    }
}

traverseDescendants

fun DelegatableNode.traverseDescendants(
    key: Any?,
    block: (TraversableNode) -> TraversableNode.Companion.TraverseDescendantsAction
): Unit

Conditionally executes block for each descendant with a matching key.

Note 1: For nodes that do not have the same key, it will continue to execute the block for descendants below that non-matching node (where there may be a node that matches).

Note 2: The parameter block's return value TraverseDescendantsAction will determine the next step in the traversal.

import androidx.compose.ui.node.traverseDescendants

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseDescendants(traverseKey) {
        if (it is CustomTraversableModifierNode) {
            it.doSomethingWithDescendant()
        }

        // [traverseDescendants()] actually has three options:
        // - ContinueTraversal
        // - SkipSubtreeAndContinueTraversal - rarely used
        // - CancelTraversal
        // They are pretty self explanatory. Usually, you just want to continue or cancel the
        // search. In some rare cases, you might want to skip the subtree but continue searching
        // the tree.
        TraverseDescendantsAction.ContinueTraversal
    }
}