CompositionLocalConsumerModifierNode.kt

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.compose.ui.node

import androidx.compose.runtime.CompositionLocal
import androidx.compose.ui.Modifier

/**
 * Implementing this interface allows your [Modifier.Node] subclass to read
 * [CompositionLocals][CompositionLocal] via the [currentValueOf] function. The values of each
 * CompositionLocal will be resolved based on the context of the layout node that the modifier is
 * attached to, meaning that the modifier will see the same values of each CompositionLocal as its
 * corresponding layout node.
 *
 * @sample androidx.compose.ui.samples.CompositionLocalConsumingModifierSample
 *
 * @see Modifier.Node
 * @see CompositionLocal
 */
interface CompositionLocalConsumerModifierNode : DelegatableNode

/**
 * Returns the current value of [local] at the position in the composition hierarchy of this
 * modifier's attached layout node.
 *
 * CompositionLocals should only be read with [currentValueOf] during the main phase of your
 * modifier's operations. This main phase of a modifier is defined as the timeframe of your Modifier
 * after it has been [attached][Modifier.Node.onAttach] and before it is
 * [detached][Modifier.Node.onDetach]. The main phase is when you will receive calls to your
 * modifier's primary hooks like [DrawModifierNode.draw], [LayoutModifierNode.measure],
 * [PointerInputModifierNode.onPointerEvent], etc. Every callback of a modifier that influences the
 * composable and is called after `onAttach()` and before `onDetach()` is considered part of the
 * main phase.
 *
 * 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.
 *
 * Avoid reading CompositionLocals in [onAttach()][Modifier.Node.onAttach] and
 * [onDetach()][Modifier.Node.onDetach]. These lifecycle callbacks only happen once, meaning that
 * any reads in a lifecycle event will yield the value of the CompositionLocal as it was during the
 * event, and then never again. This can lead to Modifiers using stale CompositionLocal values and
 * unexpected behaviors in the UI.
 *
 * This function will fail with an [IllegalStateException] if you attempt to read a CompositionLocal
 * before the node is [attached][Modifier.Node.onAttach] or after the node is
 * [detached][Modifier.Node.onDetach].
 */
fun <T> CompositionLocalConsumerModifierNode.currentValueOf(local: CompositionLocal<T>): T {
    check(node.isAttached) {
        "Cannot read CompositionLocal because the Modifier node is not currently attached. Make " +
            "sure to only invoke currentValueOf() in the main phase of your modifier. See " +
            "currentValueOf()'s documentation for more information."
    }
    return requireLayoutNode().compositionLocalMap[local]
}