androidx.compose.ui.node

Interfaces

CompositionLocalConsumerModifierNode

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

Cmn
DelegatableNode

Represents a Modifier.Node which can be a delegate of another Modifier.Node.

Cmn
DrawModifierNode

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

Cmn
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.

Cmn
InteroperableComposeUiNode

This interface allows the layout inspector to access inspect instances of Views that are associated with a Compose UI node.

Cmn
LayoutAwareModifierNode

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

Cmn
LayoutModifierNode

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

Cmn
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.

Cmn
ParentDataModifierNode

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

Cmn
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.

Cmn
RootForTest

The marker interface to be implemented by the root backing the composition.

Cmn
SemanticsModifierNode

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

Cmn
TraversableNode

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

Cmn

Classes

DelegatingNode

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

Cmn
DpTouchBoundsExpansion

Describes the expansion of a PointerInputModifierNode's touch bounds along each edges using Dp for units.

Cmn
ModifierNodeElement

A Modifier.Element which manages an instance of a particular Modifier.Node implementation.

Cmn
Ref

Value holder general purpose class.

Cmn
TouchBoundsExpansion

Describes the expansion of a PointerInputModifierNode's touch bounds along each edges.

Cmn

Objects

Annotations

Enums

TraversableNode.Companion.TraverseDescendantsAction

Tree traversal actions for the traverseDescendantsIf related functions:

Cmn

Top-level functions summary

DpTouchBoundsExpansion
DpTouchBoundsExpansion(start: Dp, top: Dp, end: Dp, bottom: Dp)

Creates a DpTouchBoundsExpansion that's aware of LayoutDirection.

Cmn
TouchBoundsExpansion
TouchBoundsExpansion(start: Int, top: Int, end: Int, bottom: Int)

Creates a TouchBoundsExpansion that's aware of LayoutDirection.

Cmn

Extension functions summary

T

Returns the current value of local at the position in the composition hierarchy of this modifier's attached layout node.

Cmn
T?

Finds the nearest ancestor of the same class and key.

Cmn
TraversableNode?

Finds the nearest traversable ancestor with a matching key.

Cmn
Unit

Invalidates this modifier's draw layer, ensuring that a draw pass will be run on the next frame.

Cmn
Unit

This will invalidate the current node's layer, and ensure that the layer is redrawn for the next frame.

Cmn
Unit

This invalidates the current node's measure result, and ensures that a re-measurement (the measurement block rerun) of this node will happen for the next frame.

Cmn
Unit

This invalidates the current node's parent data, and ensures that layouts that utilize it will be scheduled to relayout for the next frame.

Cmn
Unit

This will invalidate the current node's placement result, and ensure that relayout (the placement block rerun) of this node will happen for the next frame .

Cmn
Unit

Invalidate semantics associated with this node.

Cmn
Unit

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

Cmn
Unit

Use this function to observe snapshot reads for any target within the specified block.

Cmn
Unit

Performs the node remeasuring synchronously even if the node was not marked as needs remeasure before.

Cmn
Density

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

Cmn
GraphicsContext

Returns the current GraphicsContext of the Owner

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
Unit
<T : TraversableNode> T.traverseAncestors(block: (T) -> Boolean)

Executes block for all ancestors of the same class and key.

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

Executes block for all ancestors with a matching key.

Cmn
Unit
<T : TraversableNode> T.traverseChildren(block: (T) -> Boolean)

Executes block for all direct children of the node that are of the same class.

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 of the same class.

Cmn
Unit

Conditionally executes block for each descendant with a matching key.

Cmn

Top-level functions

DpTouchBoundsExpansion

fun DpTouchBoundsExpansion(
    start: Dp = 0.dp,
    top: Dp = 0.dp,
    end: Dp = 0.dp,
    bottom: Dp = 0.dp
): DpTouchBoundsExpansion

Creates a DpTouchBoundsExpansion that's aware of LayoutDirection. See DpTouchBoundsExpansion.start and DpTouchBoundsExpansion.end for more details about LayoutDirection.

The start, top, end and bottom represent the distance that the touch bounds is expanded along the corresponding edge.

TouchBoundsExpansion

fun TouchBoundsExpansion(start: Int = 0, top: Int = 0, end: Int = 0, bottom: Int = 0): TouchBoundsExpansion

Creates a TouchBoundsExpansion that's aware of LayoutDirection. See TouchBoundsExpansion.start and TouchBoundsExpansion.end for more details about LayoutDirection.

The start, top, end and bottom represent the amount of pixels that the touch bounds is expanded along the corresponding edge. Each value must be in the range of 0 to 32767 (inclusive).

Extension functions

currentValueOf

fun <T : Any?> CompositionLocalConsumerModifierNode.currentValueOf(
    local: CompositionLocal<T>
): T

Returns the current value of local at the position in the composition hierarchy of this modifier's attached layout node.

Unlike CompositionLocal.current, reads via this function are not automatically tracked by Compose. Modifiers are not able to recompose in the same way that a Composable can, and therefore can't receive updates arbitrarily for a CompositionLocal.

Because CompositionLocals may change arbitrarily, it is strongly recommended to ensure that the composition local is observed instead of being read once. If you call currentValueOf inside of a modifier callback like LayoutModifierNode.measure or DrawModifierNode.draw, then Compose will track the CompositionLocal read. This happens automatically, because these Compose UI phases take place in a snapshot observer that tracks which states are read. If the value of the CompositionLocal changes, and it was read inside of the measure or draw phase, then that phase will automatically be invalidated.

For all other reads of a CompositionLocal, this function will not notify you when the value of the local changes. Modifier.Node classes that also implement ObserverModifierNode may observe CompositionLocals arbitrarily by performing the lookup in an observeReads block. To continue observing values of the CompositionLocal, it must be read again in an observeReads block during or after the ObserverModifierNode.onObservedReadsChanged callback is invoked. See below for an example of how to implement this observation pattern.

import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.node.observeReads

val LocalValue = compositionLocalOf { "abc123" }
class ValueObserverModifierNode :
    Modifier.Node(), CompositionLocalConsumerModifierNode, ObserverModifierNode {
    private var observedValue: String? = null

    override fun onAttach() {
        onObservedReadsChanged()
    }

    override fun onDetach() {
        observedValue = null
    }

    override fun onObservedReadsChanged() {
        observeReads {
            observedValue = currentValueOf(LocalValue)
            // Do something with the new value
        }
    }
}

This function will fail with an IllegalStateException if you attempt to read a CompositionLocal before the node is attached or after the node is detached.

Parameters
local: CompositionLocal<T>

The CompositionLocal to get the current value of

Returns
T

The value provided by the nearest CompositionLocalProvider component that invokes, directly or indirectly, the composable function that this modifier is attached to. If local was never provided, its default value will be returned instead.

findNearestAncestor

fun <T : TraversableNode> T.findNearestAncestor(): T?

Finds the nearest ancestor of the same class and key.

findNearestAncestor

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

Finds the nearest traversable ancestor with a matching key.

invalidateDraw

fun DrawModifierNode.invalidateDraw(): Unit

Invalidates this modifier's draw layer, ensuring that a draw pass will be run on the next frame.

invalidateLayer

fun LayoutModifierNode.invalidateLayer(): Unit

This will invalidate the current node's layer, and ensure that the layer is redrawn for the next frame.

invalidateMeasurement

fun LayoutModifierNode.invalidateMeasurement(): Unit

This invalidates the current node's measure result, and ensures that a re-measurement (the measurement block rerun) of this node will happen for the next frame.

invalidateParentData

fun ParentDataModifierNode.invalidateParentData(): Unit

This invalidates the current node's parent data, and ensures that layouts that utilize it will be scheduled to relayout for the next frame.

invalidatePlacement

fun LayoutModifierNode.invalidatePlacement(): Unit

This will invalidate the current node's placement result, and ensure that relayout (the placement block rerun) of this node will happen for the next frame .

invalidateSemantics

fun SemanticsModifierNode.invalidateSemantics(): Unit

Invalidate semantics associated with this node. This will reset the SemanticsConfiguration associated with the layout node backing this modifier node, and will re-calculate it the next time the SemanticsConfiguration is read.

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.

observeReads

fun <T : Modifier.Node & ObserverModifierNode> T.observeReads(block: () -> Unit): Unit

Use this function to observe snapshot reads for any target within the specified block. ObserverModifierNode.onObservedReadsChanged is called when any of the observed values within the snapshot change.

remeasureSync

fun LayoutModifierNode.remeasureSync(): Unit

Performs the node remeasuring synchronously even if the node was not marked as needs remeasure before. Useful for cases like when during scrolling you need to re-execute the measure block to consume the scroll offset and remeasure your children in a blocking way.

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.

requireGraphicsContext

fun DelegatableNode.requireGraphicsContext(): GraphicsContext

Returns the current GraphicsContext of the Owner

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.

traverseAncestors

fun <T : TraversableNode> T.traverseAncestors(block: (T) -> Boolean): Unit

Executes block for all ancestors of the same class and key.

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

import androidx.compose.ui.Modifier
import androidx.compose.ui.node.traverseAncestors

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseAncestors {
        // Because I use the existing key of the class, I can guarantee 'it' will be of the same
        // type as the class, so I can call my functions directly.
        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
    }
}

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.Modifier
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 <T : TraversableNode> T.traverseChildren(block: (T) -> Boolean): Unit

Executes block for all direct children of the node that are of the same class.

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.Modifier
import androidx.compose.ui.node.traverseChildren

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseChildren {
        // Because I use the existing key of the class, I can guarantee 'it' will be of the same
        // type as the class, so I can call my functions directly.
        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
    }
}

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.Modifier
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 <T : TraversableNode> T.traverseDescendants(
    block: (T) -> TraversableNode.Companion.TraverseDescendantsAction
): Unit

Conditionally executes block for each descendant of the same class.

Note 1: For nodes that do not have the same key, it will continue to execute the block for the 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.Modifier
import androidx.compose.ui.node.TraversableNode.Companion.TraverseDescendantsAction
import androidx.compose.ui.node.traverseDescendants

val customTraversableModifierNode = CustomTraversableModifierNode()

with(customTraversableModifierNode) {
    traverseDescendants {
        // Because I use the existing key of the class, I can guarantee 'it' will be of the same
        // type as the class, so I can call my functions directly.
        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
    }
}

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.Modifier
import androidx.compose.ui.node.TraversableNode.Companion.TraverseDescendantsAction
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
    }
}