androidx.compose.runtime.retain

Interfaces

RetainObserver

Objects implementing this interface are notified of their usage in retain.

Cmn
RetainStateProvider

RetainStateProvider is an owner of the isKeepingExitedValues state used by RetainScope.

Cmn
RetainStateProvider.RetainStateObserver

Listener interface to observe changes in the value of RetainStateProvider.isKeepingExitedValues.

Cmn
RetainedEffectResult

The return type of a built RetainedEffect.

Cmn

Classes

ControlledRetainScope

A ControlledRetainScope is effectively a "Mutable" RetainScope.

Cmn
RetainScope

A RetainScope acts as a storage area for objects being retained.

Cmn
RetainScopeHolder

A RetainScopeHolder creates and manages RetainScope instances for collections of items.

Cmn
RetainedEffectScope

Receiver scope for RetainedEffect that offers the onRetire clause that should be the last statement in any call to RetainedEffect.

Cmn

Objects

ForgetfulRetainScope

The ForgetfulRetainScope is an implementation of RetainScope that is incapable of keeping any exited values.

Cmn
RetainStateProvider.AlwaysKeepExitedValues

An implementation of RetainStateProvider that is not backed by a RetainScope and is always set to keep exited values.

Cmn
RetainStateProvider.NeverKeepExitedValues

An implementation of RetainStateProvider that is not backed by a RetainScope and is never set to keep exited values.

Cmn

Top-level functions summary

Unit

RetainedContentHost is used to install a RetainScope around a block of content.

Cmn
Unit

This function is deprecated. RetainedEffect must provide one or more 'key' parameters that define the identity of the RetainedEffect and determine when its previous effect should be disposed and a new effect started for the new key.

Cmn
Unit

A side effect of composition that must run for any new unique value of key1 and must be reversed or cleaned up if key1 changes or if the RetainedEffect permanently leaves composition.

Cmn
Unit

A side effect of composition that must run for any new unique value of keys and must be reversed or cleaned up if keys changes or if the RetainedEffect permanently leaves composition.

Cmn
Unit

A side effect of composition that must run for any new unique value of key1 or key2 and must be reversed or cleaned up if key1 or key2 changes or if the RetainedEffect permanently leaves composition.

Cmn
Unit
@Composable
@NonRestartableComposable
RetainedEffect(
    key1: Any?,
    key2: Any?,
    key3: Any?,
    effect: RetainedEffectScope.() -> RetainedEffectResult
)

A side effect of composition that must run for any new unique value of key1, key2, or key3 and must be reversed or cleaned up if key1, key2, or key3 changes or if the RetainedEffect permanently leaves composition.

Cmn
inline T
@Composable
<T : Any?> retain(noinline calculation: () -> T)

Remember the value produced by calculation and retain it in the current RetainScope.

Cmn
inline T
@Composable
<T : Any?> retain(vararg keys: Any?, noinline calculation: () -> T)

Remember the value produced by calculation and retain it in the current RetainScope.

Cmn
ControlledRetainScope

Retains a ControlledRetainScope that is nested under the current LocalRetainScope and has no other defined retention scenarios.

Cmn
RetainScopeHolder

Returns a retain instance of a new RetainScopeHolder.

Cmn

Top-level properties summary

ProvidableCompositionLocal<RetainScope>

The RetainScope in which retain values will be tracked in.

Cmn

Top-level functions

RetainedContentHost

@Composable
fun RetainedContentHost(active: Boolean, content: @Composable () -> Unit): Unit

RetainedContentHost is used to install a RetainScope around a block of content. The installed RetainScope is managed such that the scope will start to keep exited values when active is false, and stop keeping exited values when active becomes true. See RetainScope.isKeepingExitedValues for more information on this terminology.

RetainedContentHost is designed as an out-of-the-box solution for managing content that's controlled effectively by an if/else statement. The content provided to this lambda will render when active is true, and be removed when active is false. If the content is hidden and then shown again in this way, the installed RetainScope will restore all retained values from the last time the content was shown.

The managed RetainScope is also retained. If this composable is removed while the parent scope is keeping its exited values, this scope will be persisted so that it can be restored in the future. If this composable is removed while its parent scope is not keeping its exited values, the scope will be discarded and all its held values will be immediately retired.

For this reason, when using this as a mechanism to retain values for content that is being shown and hidden, this composable must be hoisted high enough so that it is not removed when the content being retained is hidden.

import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.RetainedContentHost
import androidx.compose.runtime.retain.RetainedEffect
import androidx.compose.runtime.retain.retain

@Composable
fun CollapsingMediaPlayer(visible: Boolean) {
    // This content is only shown when `visible == true`
    RetainedContentHost(active = visible) {
        // Create a media player that will be retained when the CollapsingMediaPlayer is no
        // longer visible. This component can continue to play audio when the video is hidden.
        val mediaPlayer = retain { MediaPlayer() }
        RetainedEffect(mediaPlayer) {
            mediaPlayer.play()
            onRetire { mediaPlayer.stop() }
        }

        // Render the video component inside the RetainedContentHost.
    }
}
Parameters
active: Boolean

Whether this host should compose its content. When this value is true, content will be rendered and the installed RetainScope will not keep exited values. When this value is false, content will stop being rendered and the installed RetainScope will collect and keep its exited values for future restoration.

content: @Composable () -> Unit

The content to render. Inside of this lambda, LocalRetainScope is set to the RetainScope managed by this composable.

@Composable
@NonRestartableComposable
fun RetainedEffect(effect: RetainedEffectScope.() -> RetainedEffectResult): Unit
@Composable
@NonRestartableComposable
fun RetainedEffect(key1: Any?, effect: RetainedEffectScope.() -> RetainedEffectResult): Unit

A side effect of composition that must run for any new unique value of key1 and must be reversed or cleaned up if key1 changes or if the RetainedEffect permanently leaves composition.

A RetainedEffect tracks the lifecycle of retained content. If the current RetainScope is keeping values because its managed content is being transiently destroyed, the RetainedEffect is kept alive. From this state, the RetainedEffect can either:

  • Be retired because the RetainScope is destroyed without its content being restored

  • Be retired if the RetainScope's content re-enters the composition but does not include this RetainedEffect or invokes it with different keys

  • Be restored to the recreated composition hierarchy. In this case, the RetainedEffect does not execute any callbacks.

If a RetainedEffect is removed from the composition hierarchy when the RetainScope is not keeping exited values, then the scope will immediately be retired and behave like a androidx.compose.runtime.DisposableEffect. Retirement has the same timing guarantees as RetainObserver.onRetired.

A RetainedEffect's key is a value that defines the identity of the RetainedEffect. If a RetainedEffect is recomposed with different keys, a new effect will be created and the previous effect will be retired. If the current RetainScope is not keeping exited values, the retirement happens before the new effect is started. Otherwise, the prior instance of the effect will continue to be retained for possible restoration until the scope stops keeping exited values.

RetainedEffect may be used to initialize or subscribe to a key and reinitialize when a different key is provided. For example:

import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.RetainedEffect
import androidx.compose.runtime.retain.retain

@Composable
fun VideoPlayer(mediaUri: String) {
    val player = retain(mediaUri) { MediaPlayer(mediaUri) }

    // Initialize each player only once after we retain it.
    // If the uri (and therefore the player) change, we need to dispose the old player
    // and initialize the new one. Likewise, the player needs to be disposed of when
    // it stops being retained.
    RetainedEffect(player) {
        player.initialize()
        onRetire { player.close() }
    }

    // ...
}

A RetainedEffect must include a retire clause as the final statement in its effect block. If your operation does not require disposal it might be a androidx.compose.runtime.SideEffect instead, or a androidx.compose.runtime.LaunchedEffect if it launches a coroutine that should be managed by the composition.

There is guaranteed to be one call to retire for every call to effect. Both effect and retire will always be run on the composition's apply dispatcher and appliers are never run concurrent with themselves, one another, applying changes to the composition tree, or running androidx.compose.runtime.RememberObserver event callbacks.

@Composable
@NonRestartableComposable
fun RetainedEffect(vararg keys: Any?, effect: RetainedEffectScope.() -> RetainedEffectResult): Unit

A side effect of composition that must run for any new unique value of keys and must be reversed or cleaned up if keys changes or if the RetainedEffect permanently leaves composition.

A RetainedEffect tracks the lifecycle of retained content. If the current RetainScope is keeping values because its managed content is being transiently destroyed, the RetainedEffect is kept alive. From this state, the RetainedEffect can either:

  • Be retired because the RetainScope is destroyed without its content being restored

  • Be retired if the RetainScope's content re-enters the composition but does not include this RetainedEffect or invokes it with different keys

  • Be restored to the recreated composition hierarchy. In this case, the RetainedEffect does not execute any callbacks.

If a RetainedEffect is removed from the composition hierarchy when the RetainScope is not keeping exited values, then the scope will immediately be retired and behave like a DisposableEffect. Retirement has the same timing guarantees as RetainObserver.onRetired.

A RetainedEffect's key is a value that defines the identity of the RetainedEffect. If a RetainedEffect is recomposed with different keys, a new effect will be created and the previous effect will be retired. If the current RetainScope is not keeping exited values, the retirement happens before the new effect is started. Otherwise, the prior instance of the effect will continue to be retained for possible restoration until the scope stops keeping exited values.

RetainedEffect may be used to initialize or subscribe to a key and reinitialize when a different key is provided. For example:

import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.RetainedEffect
import androidx.compose.runtime.retain.retain

@Composable
fun VideoPlayer(mediaUri: String) {
    val player = retain(mediaUri) { MediaPlayer(mediaUri) }

    // Initialize each player only once after we retain it.
    // If the uri (and therefore the player) change, we need to dispose the old player
    // and initialize the new one. Likewise, the player needs to be disposed of when
    // it stops being retained.
    RetainedEffect(player) {
        player.initialize()
        onRetire { player.close() }
    }

    // ...
}

A RetainedEffect must include a retire clause as the final statement in its effect block. If your operation does not require disposal it might be a androidx.compose.runtime.SideEffect instead, or a androidx.compose.runtime.LaunchedEffect if it launches a coroutine that should be managed by the composition.

There is guaranteed to be one call to retire for every call to effect. Both effect and retire will always be run on the composition's apply dispatcher and appliers are never run concurrent with themselves, one another, applying changes to the composition tree, or running androidx.compose.runtime.RememberObserver event callbacks.

@Composable
@NonRestartableComposable
fun RetainedEffect(key1: Any?, key2: Any?, effect: RetainedEffectScope.() -> RetainedEffectResult): Unit

A side effect of composition that must run for any new unique value of key1 or key2 and must be reversed or cleaned up if key1 or key2 changes or if the RetainedEffect permanently leaves composition.

A RetainedEffect tracks the lifecycle of retained content. If the current RetainScope is keeping values because its managed content is being transiently destroyed, the RetainedEffect is kept alive. From this state, the RetainedEffect can either:

  • Be retired because the RetainScope is destroyed without its content being restored

  • Be retired if the RetainScope's content re-enters the composition but does not include this RetainedEffect or invokes it with different keys

  • Be restored to the recreated composition hierarchy. In this case, the RetainedEffect does not execute any callbacks.

If a RetainedEffect is removed from the composition hierarchy when the RetainScope is not keeping exited values, then the scope will immediately be retired and behave like a androidx.compose.runtime.DisposableEffect. Retirement has the same timing guarantees as RetainObserver.onRetired.

A RetainedEffect's key is a value that defines the identity of the RetainedEffect. If a RetainedEffect is recomposed with different keys, a new effect will be created and the previous effect will be retired. If the current RetainScope is not keeping exited values, the retirement happens before the new effect is started. Otherwise, the prior instance of the effect will continue to be retained for possible restoration until the scope stops keeping exited values.

RetainedEffect may be used to initialize or subscribe to a key and reinitialize when a different key is provided. For example:

import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.RetainedEffect
import androidx.compose.runtime.retain.retain

@Composable
fun VideoPlayer(mediaUri: String) {
    val player = retain(mediaUri) { MediaPlayer(mediaUri) }

    // Initialize each player only once after we retain it.
    // If the uri (and therefore the player) change, we need to dispose the old player
    // and initialize the new one. Likewise, the player needs to be disposed of when
    // it stops being retained.
    RetainedEffect(player) {
        player.initialize()
        onRetire { player.close() }
    }

    // ...
}

A RetainedEffect must include a retire clause as the final statement in its effect block. If your operation does not require disposal it might be a androidx.compose.runtime.SideEffect instead, or a androidx.compose.runtime.LaunchedEffect if it launches a coroutine that should be managed by the composition.

There is guaranteed to be one call to retire for every call to effect. Both effect and retire will always be run on the composition's apply dispatcher and appliers are never run concurrent with themselves, one another, applying changes to the composition tree, or running androidx.compose.runtime.RememberObserver event callbacks.

@Composable
@NonRestartableComposable
fun RetainedEffect(
    key1: Any?,
    key2: Any?,
    key3: Any?,
    effect: RetainedEffectScope.() -> RetainedEffectResult
): Unit

A side effect of composition that must run for any new unique value of key1, key2, or key3 and must be reversed or cleaned up if key1, key2, or key3 changes or if the RetainedEffect permanently leaves composition.

A RetainedEffect tracks the lifecycle of retained content. If the current RetainScope is keeping values because its managed content is being transiently destroyed, the RetainedEffect is kept alive. From this state, the RetainedEffect can either:

  • Be retired because the RetainScope is destroyed without its content being restored

  • Be retired if the RetainScope's content re-enters the composition but does not include this RetainedEffect or invokes it with different keys

  • Be restored to the recreated composition hierarchy. In this case, the RetainedEffect does not execute any callbacks.

If a RetainedEffect is removed from the composition hierarchy when the RetainScope is not keeping exited values, then the scope will immediately be retired and behave like a androidx.compose.runtime.DisposableEffect. Retirement has the same timing guarantees as RetainObserver.onRetired.

A RetainedEffect's key is a value that defines the identity of the RetainedEffect. If a RetainedEffect is recomposed with different keys, a new effect will be created and the previous effect will be retired. If the current RetainScope is not keeping exited values, the retirement happens before the new effect is started. Otherwise, the prior instance of the effect will continue to be retained for possible restoration until the scope stops keeping exited values.

RetainedEffect may be used to initialize or subscribe to a key and reinitialize when a different key is provided. For example:

import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.RetainedEffect
import androidx.compose.runtime.retain.retain

@Composable
fun VideoPlayer(mediaUri: String) {
    val player = retain(mediaUri) { MediaPlayer(mediaUri) }

    // Initialize each player only once after we retain it.
    // If the uri (and therefore the player) change, we need to dispose the old player
    // and initialize the new one. Likewise, the player needs to be disposed of when
    // it stops being retained.
    RetainedEffect(player) {
        player.initialize()
        onRetire { player.close() }
    }

    // ...
}

A RetainedEffect must include a retire clause as the final statement in its effect block. If your operation does not require disposal it might be a androidx.compose.runtime.SideEffect instead, or a androidx.compose.runtime.LaunchedEffect if it launches a coroutine that should be managed by the composition.

There is guaranteed to be one call to retire for every call to effect. Both effect and retire will always be run on the composition's apply dispatcher and appliers are never run concurrent with themselves, one another, applying changes to the composition tree, or running androidx.compose.runtime.RememberObserver event callbacks.

@Composable
inline fun <T : Any?> retain(noinline calculation: () -> T): T

Remember the value produced by calculation and retain it in the current RetainScope. A retained value is one that is persisted in memory to survive transient destruction and recreation of a portion or the entirety of the content in the composition hierarchy. Some examples of when content is transient destroyed occur include:

  • Navigation destinations that are on the back stack, not currently visible, and not composed

  • UI components that are collapsed, not rendering, and not composed

  • On Android, composition hierarchies hosted by an Activity that is being destroyed and recreated due to a configuration change

When a value retained by retain leaves the composition hierarchy during one of these retention scenarios, the LocalRetainScope will persist it until the content is recreated. If an instance of this function then re-enters the composition hierarchy during the recreation, it will return the retained value instead of invoking calculation again.

If this function leaves the composition hierarchy when the LocalRetainScope is not keeping values that exit the composition, the value will be discarded immediately.

The lifecycle of the retained value can be observed by implementing RetainObserver. Callbacks from RememberObserver are never invoked on objects retained this way. It is invalid to retain an object that is a RememberObserver but not a RetainObserver, and an exception will be thrown.

The lifecycle of a retained value is shown in the diagram below. This diagram tracks how a retained value is held through its lifecycle and when it transitions between states.

┌──────────────────────┐

retain(keys) { ... }
┌────────────┐│
└────────┤
value: T ├┘
└──┬─────────┘

Exit Enter
composition composition
or change
keys ┌──────────────────────────┐
├───No retained value─────┤ calculation: () -> T
or different keys └──────────────────────────┘
┌──────────────────────────┐
└───Re-enter composition──┤ Local RetainScope
with the same keys └─────────────────┬────────┘

┌─Yes────────────────┘ value not
restored and
.──────────────────┴──────────────────. scope stops
└─▶( RetainScope.isKeepingExitedValues ) keeping exited
`──────────────────┬──────────────────' values

┌──────────────────────────┐
└─No──▶│ value is retired
└──────────────────────────┘

Important: Retained values are held longer than the lifespan of the composable they are associated with. This can cause memory leaks if a retained object is kept beyond its expected lifetime. Be cautious with the types of data that you retain. Never retain an Android Context or an object that references a Context (including View), either directly or indirectly. To mark that a custom class should not be retained (possibly because it will cause a memory leak), you can annotate your class definition with androidx.compose.runtime.annotation.DoNotRetain.

Parameters
noinline calculation: () -> T

A computation to invoke to create a new value, which will be used when a previous one is not available to return because it was neither remembered nor retained.

Returns
T

The result of calculation

Throws
kotlin.IllegalArgumentException

if the return result of calculation both implements RememberObserver and does not also implement RetainObserver

See also
remember
@Composable
inline fun <T : Any?> retain(vararg keys: Any?, noinline calculation: () -> T): T

Remember the value produced by calculation and retain it in the current RetainScope. A retained value is one that is persisted in memory to survive transient destruction and recreation of a portion of the entirety of the composition hierarchy. Some examples of when this transient destruction occur include:

  • Navigation destinations that are on the back stack, not currently visible, and not composed

  • UI components that are collapsed, not rendering, and not composed

  • On Android, composition hierarchies hosted by an Activity that is being destroyed and recreated due to a configuration change

When a value retained by retain leaves the composition hierarchy during one of these retention scenarios, the LocalRetainScope will persist it until the content is recreated. If an instance of this function then re-enters the composition hierarchy during the recreation, it will return the retained value instead of invoking calculation again.

If this function leaves the composition hierarchy when the LocalRetainScope is not keeping values that exit the composition or is invoked with list of keys that are not all equal (==) to the values they had in the previous composition, the value will be discarded immediately and calculation will execute again when a new value is needed.

The lifecycle of the retained value can be observed by implementing RetainObserver. Callbacks from RememberObserver are never invoked on objects retained this way. It is illegal to retain an object that is a RememberObserver but not a RetainObserver.

Keys passed to this composable will be kept in-memory while the computed value is retained for comparison against the old keys until the value is retired. Keys are allowed to implement RememberObserver arbitrarily, unlike the values returned by calculation. If a key implements RetainObserver, it will not receive retention callbacks from this usage.

The lifecycle of a retained value is shown in the diagram below. This diagram tracks how a retained value is held through its lifecycle and when it transitions between states.

┌──────────────────────┐

retain(keys) { ... }
┌────────────┐│
└────────┤
value: T ├┘
└──┬─────────┘

Exit Enter
composition composition
or change
keys ┌──────────────────────────┐
├───No retained value─────┤ calculation: () -> T
or different keys └──────────────────────────┘
┌──────────────────────────┐
└───Re-enter composition──┤ Local RetainScope
with the same keys └─────────────────┬────────┘

┌─Yes────────────────┘ value not
restored and
.──────────────────┴──────────────────. scope stops
└─▶( RetainScope.isKeepingExitedValues ) keeping exited
`──────────────────┬──────────────────' values

┌──────────────────────────┐
└─No──▶│ value is retired
└──────────────────────────┘

Important: Retained values are held longer than the lifespan of the composable they are associated with. This can cause memory leaks if a retained object is kept beyond its expected lifetime. Be cautious with the types of data that you retain. Never retain an Android Context or an object that references a Context (including View), either directly or indirectly. To mark that a custom class should not be retained (possibly because it will cause a memory leak), you can annotate your class definition with androidx.compose.runtime.annotation.DoNotRetain.

Parameters
vararg keys: Any?

An arbitrary list of keys that, if changed, will cause an old retained value to be discarded and for calculation to return a new value, regardless of whether the old value was being retained in the RetainScope or not.

noinline calculation: () -> T

A producer that will be invoked to initialize the retained value if a value from the previous composition isn't available.

Returns
T

The result of calculation

Throws
kotlin.IllegalArgumentException

if the return result of calculation both implements RememberObserver and does not also implement RetainObserver

See also
remember

retainControlledRetainScope

@Composable
fun retainControlledRetainScope(): ControlledRetainScope

Retains a ControlledRetainScope that is nested under the current LocalRetainScope and has no other defined retention scenarios.

A ControlledRetainScope created in this way will mirror the retention behavior of LocalRetainScope. When the parent scope begins retaining its values, the returned scope will receive a request to start retaining values as well. When the parent scope stops retaining values, that request is cleared.

This API is available as a building block for other retain scopes defined in composition. To define your own retention scenario, call ControlledRetainScope.startKeepingExitedValues and ControlledRetainScope.stopKeepingExitedValues on the returned scope as appropriate. You must also install this scope in the composition hierarchy by providing it as the value of LocalRetainScope.

When this value stops being retained, it will automatically stop keeping exited values, regardless of how many times ControlledRetainScope.startKeepingExitedValues was called.

import androidx.compose.animation.AnimatedContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.retain.LocalRetainScope
import androidx.compose.runtime.retain.RetainedContentHost
import androidx.compose.runtime.retain.retain
import androidx.compose.runtime.retain.retainControlledRetainScope

@Composable
fun AnimatedRetainedContentHost(active: Boolean, content: @Composable () -> Unit) {
    // Create a retain scope. It will be added as a child to the current scope and start
    // keeping exited values when the parent does. On Android, this scope will implicitly
    // survive and forward retention events caused by configuration changes.
    val retainScope = retainControlledRetainScope()
    AnimatedContent(active) { targetState ->
        if (targetState) {
            // Install the retain scope over the child content
            CompositionLocalProvider(LocalRetainScope provides retainScope) {
                // Values retained here will be kept when this content is faded out,
                // and restored when the content is added back to the composition.
                content()
            }

            // Define the retention scenario that will issue commands to start and stop keeping
            // exited values. If you use this effect in your code, it must come AFTER the
            // content is composed to correctly capture values. This effect is not mandatory,
            // but is a convenient way to match the RetainScope's state to the visibility of its
            // content. You can manage the retain scope in any way suitable for your content.
            val composer = currentComposer
            DisposableEffect(retainScope) {
                // Stop keeping exited values when we become active. Use the request count to
                // only look at our state and to ignore any parent-influenced requests.
                val cancellationHandle =
                    if (retainScope.keepExitedValuesRequestsFromSelf > 0) {
                        composer.scheduleFrameEndCallback {
                            retainScope.stopKeepingExitedValues()
                        }
                    } else {
                        null
                    }

                onDispose {
                    // Start keeping exited values when we deactivate
                    cancellationHandle?.cancel()
                    retainScope.startKeepingExitedValues()
                }
            }
        }
    }
}
Returns
ControlledRetainScope

A ControlledRetainScope nested under the LocalRetainScope, ready to be installed in the composition hierarchy and be used to define a retention scenario.

retainRetainScopeHolder

@Composable
fun retainRetainScopeHolder(): RetainScopeHolder

Returns a retain instance of a new RetainScopeHolder. A RetainScopeHolder is a container of RetainScopes that allows a parent composable to have children with different retention lifecycles. See RetainScopeHolder for more information on how to use this class, including a sample.

The returned provider will be parented to the LocalRetainScope at this point in the composition hierarchy. If the LocalRetainScope is changed, the returned provider will be re-parented to the new LocalRetainScope. When this invocation leaves composition, it will continue retaining if its parent scope was retaining. When this RetainScopeHolder is retired, its child scopes will also be retired and the scope will be disposed.

This method is intended to be used for managing retain state in composables that swap in and out children arbitrarily.

Top-level properties

LocalRetainScope

val LocalRetainScopeProvidableCompositionLocal<RetainScope>

The RetainScope in which retain values will be tracked in. Since a RetainScope controls retention scenarios and signals when to start and end the retention of objects removed from composition, a composition hierarchy may have several RetainScopes to introduce retention periods to specific pieces of content.

The default implementation is a ForgetfulRetainScope that causes retain to behave the same as remember. On Android, a lifecycle-aware scope is installed at the root of the composition that retains values across configuration changes.

If this CompositionLocal is updated, all values previously returned by retain will be adopted to the new scope and will follow the new scope's retention lifecycle.

RetainScopes should be installed so that their tracked transiently removed content is always removed from composition in the same frame (and by extension, all retained values leave composition in the same frame). If the RetainScope starts keeping exited values and its tracked content is removed in an arbitrary order across several recompositions, it may cause retained values to be restored incorrectly if the retained values from different regions in the composition have the same currentCompositeKeyHashCode.