[go: nahoru, domu]

blob: 9322931206b89075f644be7799689d9a5ead17d9 [file] [log] [blame]
/*
* Copyright 2019 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.runtime
/**
* Compose passes data through the composition tree explicitly through means of parameters to
* composable functions. This is often times the simplest and best way to have data flow through
* the tree.
*
* Sometimes this model can be cumbersome or break down for data that is needed by lots of
* components, or when components need to pass data between one another but keep that implementation
* detail private. For these cases, [Ambient]s can be used as an implicit way to have data flow
* through a composition.
*
* [Ambient]s by their nature are hierarchical. They make sense when the value of the ambient needs
* to be scoped to a particular sub-hierarchy of the composition.
*
* One must create an Ambient instance, which can be referenced by the consumers statically. Ambient
* instances themselves hold no data, and can be thought of as a type-safe identifier for the data
* being passed down a tree. Ambient factory functions takes a single parameter, a factory to
* create a default value in cases where an ambient is used without a Provider. If this is a
* situation you would rather not handle, you can throw an error in this factory
*
* @sample androidx.compose.runtime.samples.createAmbient
*
* Somewhere up the tree, a [Providers] component can be used, which provides a value for the
* ambient. This would often be at the "root" of a tree, but could be anywhere, and can also be
* used in multiple places to override the provided value for a sub-tree.
*
* @sample androidx.compose.runtime.samples.ambientProvider
*
* Intermediate components do not need to know about the ambient value, and can have zero
* dependencies on it. For example, `SomeScreen` might look like this:
*
* @sample androidx.compose.runtime.samples.someScreenSample
*
* Finally, a component that wishes to consume the ambient value can use the [current]
* property of the ambient key which returns the current value of the ambient, and subscribes the
* component to changes of it.
*
* @sample androidx.compose.runtime.samples.consumeAmbient
*/
@Stable
sealed class Ambient<T> constructor(defaultFactory: (() -> T)? = null) {
@Suppress("UNCHECKED_CAST")
internal val defaultValueHolder = LazyValueHolder(defaultFactory)
@Composable
internal abstract fun provided(value: T): State<T>
/**
* Return the value provided by the nearest [Providers] component that invokes, directly or
* indirectly, the composable function that uses this property.
*
* @sample androidx.compose.runtime.samples.consumeAmbient
*/
@ComposableContract(readonly = true)
@Composable
inline val current: T get() = currentComposer.consume(this)
}
/**
* A [ProvidableAmbient] can be used in [Providers] to provide values.
*
* @see ambientOf
* @see Ambient
* @see Providers
*/
@Stable
abstract class ProvidableAmbient<T> internal constructor(defaultFactory: (() -> T)?) :
Ambient<T> (defaultFactory) {
/**
* Associates an ambient key to a value in a call to [Providers].
*
* @see Ambient
* @see ProvidableAmbient
*/
@Suppress("UNCHECKED_CAST")
infix fun provides(value: T) = ProvidedValue(this, value)
}
/**
* A [DynamicProvidableAmbient] is an ambient backed by [mutableStateOf]. Providing new values
* using a [DynamicProvidableAmbient] will provide the same [State] with a different value.
* Reading the ambient value of a [DynamicProvidableAmbient] will record a read in the
* [RecomposeScope] of the composition. Changing the provided value will invalidate the
* [RecomposeScope]s.
*
* @see ambientOf
*/
internal class DynamicProvidableAmbient<T> constructor(
private val policy: SnapshotMutationPolicy<T>,
defaultFactory: (() -> T)?
) : ProvidableAmbient<T>(defaultFactory) {
@Composable
override fun provided(value: T): State<T> = state(policy) { value }.apply {
this.value = value
}
}
/**
* A [StaticProvidableAmbient] is a value that is expected to rarely change.
*
* @see staticAmbientOf
*/
internal class StaticProvidableAmbient<T>(defaultFactory: (() -> T)?) :
ProvidableAmbient<T>(defaultFactory) {
@Composable
override fun provided(value: T): State<T> = StaticValueHolder(value)
}
/**
* Create an ambient key that can be provided using [Providers]. Changing the value provided
* during recomposition will invalidate the children of [Providers] that read the value using
* [Ambient.current].
*
* @param policy a policy to determine when an ambient is considered changed. See
* [SnapshotMutationPolicy] for details.
*
* @see Ambient
* @see staticAmbientOf
* @see mutableStateOf
*/
fun <T> ambientOf(
policy: SnapshotMutationPolicy<T> =
@OptIn(ExperimentalComposeApi::class)
referentialEqualityPolicy(),
defaultFactory: (() -> T)? = null
): ProvidableAmbient<T> = DynamicProvidableAmbient(policy, defaultFactory)
/**
* Create an ambient key that can be provided using [Providers]. Changing the value provided
* will cause the entire tree below [Providers] to be recomposed, disabling skipping of composable
* calls.
*
* A static ambient should be only be used when the value provided is highly unlikely to change.
*
* @see Ambient
* @see ambientOf
*/
fun <T> staticAmbientOf(defaultFactory: (() -> T)? = null): ProvidableAmbient<T> =
StaticProvidableAmbient(defaultFactory)
/**
* [Providers] binds values to [ProvidableAmbient] keys. Reading the ambient using
* [Ambient.current] will return the value provided in [Providers]'s [values] parameter for all
* composable functions called directly or indirectly in the [children] lambda.
*
* @sample androidx.compose.runtime.samples.ambientProvider
*
* @see Ambient
* @see ambientOf
* @see staticAmbientOf
*/
@Composable
fun Providers(vararg values: ProvidedValue<*>, children: @Composable () -> Unit) {
currentComposer.startProviders(values)
children()
currentComposer.endProviders()
}