DelegatingNode

Class

Common
abstract class DelegatingNode : Modifier.Node()

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

This can be useful to compose multiple node implementations into one.

Code Examples

DelegatedNodeSampleExplicit

@Composable
fun DelegatedNodeSampleExplicit() {
    class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
        override fun onPointerEvent(
            pointerEvent: PointerEvent,
            pass: PointerEventPass,
            bounds: IntSize,
        ) {
            // ...
        }
        override fun onCancelPointerInput() {
            // ...
        }
    }
    class TapGestureWithClickSemantics(onTap: () -> Unit) :
        PointerInputModifierNode, SemanticsModifierNode, DelegatingNode() {
        var onTap: () -> Unit
            get() = gesture.onTap
            set(value) {
                gesture.onTap = value
            }
        val gesture = delegate(TapGestureNode(onTap))
        override fun onPointerEvent(
            pointerEvent: PointerEvent,
            pass: PointerEventPass,
            bounds: IntSize,
        ) {
            gesture.onPointerEvent(pointerEvent, pass, bounds)
        }
        override fun onCancelPointerInput() {
            gesture.onCancelPointerInput()
        }
        override fun SemanticsPropertyReceiver.applySemantics() {
            onClick {
                gesture.onTap()
                true
            }
        }
    }
}

DelegatedNodeSampleImplicit

@Composable
fun DelegatedNodeSampleImplicit() {
    class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
        override fun onPointerEvent(
            pointerEvent: PointerEvent,
            pass: PointerEventPass,
            bounds: IntSize,
        ) {
            // ...
        }
        override fun onCancelPointerInput() {
            // ...
        }
    }
    class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
        override fun SemanticsPropertyReceiver.applySemantics() {
            onClick {
                onTap()
                true
            }
        }
    }
    class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
        var onTap: () -> Unit
            get() = gesture.onTap
            set(value) {
                gesture.onTap = value
                semantics.onTap = value
            }
        val gesture = delegate(TapGestureNode(onTap))
        val semantics = delegate(TapSemanticsNode(onTap))
    }
}

LazyDelegationExample

@Composable
fun LazyDelegationExample() {
    class ExpensivePositionHandlingOnPointerEvents : PointerInputModifierNode, DelegatingNode() {
        val globalAwareNode =
            object : GlobalPositionAwareModifierNode, Modifier.Node() {
                override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
                    // ...
                }
            }
        override fun onPointerEvent(
            pointerEvent: PointerEvent,
            pass: PointerEventPass,
            bounds: IntSize,
        ) {
            // wait until first pointer event to start listening to global
            // position
            if (!globalAwareNode.isAttached) {
                delegate(globalAwareNode)
            }
            // normal input processing
        }
        override fun onCancelPointerInput() {
            // ...
        }
    }
    class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
        override fun onPointerEvent(
            pointerEvent: PointerEvent,
            pass: PointerEventPass,
            bounds: IntSize,
        ) {
            // ...
        }
        override fun onCancelPointerInput() {
            // ...
        }
    }
    class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
        override fun SemanticsPropertyReceiver.applySemantics() {
            onClick {
                onTap()
                true
            }
        }
    }
    class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
        var onTap: () -> Unit
            get() = gesture.onTap
            set(value) {
                gesture.onTap = value
                semantics.onTap = value
            }
        val gesture = delegate(TapGestureNode(onTap))
        val semantics = delegate(TapSemanticsNode(onTap))
    }
}

ConditionalDelegationExample

fun ConditionalDelegationExample() {
    class MyModifierNode(global: Boolean) : DelegatingNode() {
        val globalAwareNode =
            object : GlobalPositionAwareModifierNode, Modifier.Node() {
                    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
                        // ...
                    }
                }
                .also { if (global) delegate(it) }
        var global: Boolean = global
            set(value) {
                if (global && !value) {
                    undelegate(globalAwareNode)
                } else if (!global && value) {
                    delegate(globalAwareNode)
                }
                field = value
            }
    }
}

DelegateInAttachSample

fun DelegateInAttachSample() {
    class MyModifierNode : DelegatingNode() {
        val globalAwareNode =
            object : GlobalPositionAwareModifierNode, Modifier.Node() {
                override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
                    // ...
                }
            }
        override fun onAttach() {
            // one can conditionally delegate in attach, for instance if certain conditions are met
            if (requireLayoutDirection() == LayoutDirection.Rtl) {
                delegate(globalAwareNode)
            }
        }
    }
}