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 isRetainingExitedValues state used by RetainedValuesStore.

Cmn
RetainStateProvider.RetainStateObserver

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

Cmn
RetainedEffectResult

The return type of a built RetainedEffect.

Cmn

Classes

ControlledRetainedValuesStore

A ControlledRetainedValuesStore is effectively a "Mutable" RetainedValuesStore.

Cmn
RetainedEffectScope

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

Cmn
RetainedValuesStore

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

Cmn
RetainedValuesStoreRegistry

A RetainedValuesStoreRegistry creates and manages RetainedValuesStore instances for collections of items.

Cmn

Objects

ForgetfulRetainedValuesStore

The ForgetfulRetainedValuesStore is an implementation of RetainedValuesStore that is incapable of retaining any exited values.

Cmn
RetainStateProvider.AlwaysRetainExitedValues

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

Cmn
RetainStateProvider.NeverRetainExitedValues

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

Cmn

Top-level functions summary

Unit

RetainedContentHost is used to install a RetainedValuesStore 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 RetainedValuesStore.

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

Cmn
ControlledRetainedValuesStore

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

Cmn
RetainedValuesStoreRegistry

Returns a retain instance of a new RetainedValuesStoreRegistry.

Cmn

Top-level properties summary

Top-level functions

RetainedContentHost

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

RetainedContentHost is used to install a RetainedValuesStore around a block of content. The installed RetainedValuesStore is managed such that the store will start to retain exited values when active is false, and stop retaining exited values when active becomes true. See RetainedValuesStore.isRetainingExitedValues 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 RetainedValuesStore will restore all retained values from the last time the content was shown.

The managed RetainedValuesStore is also retained. If this composable is removed while the parent store is retaining its exited values, this store will be persisted so that it can be restored in the future. If this composable is removed while its parent store is not retaining its exited values, the store 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 RetainedValuesStore will not retain exited values. When this value is false, content will stop being rendered and the installed RetainedValuesStore will collect and retain its exited values for future restoration.

content: @Composable () -> Unit

The content to render. Inside of this lambda, LocalRetainedValuesStore is set to the RetainedValuesStore 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 RetainedValuesStore is retaining 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 RetainedValuesStore is destroyed without its content being restored

  • Be retired if the RetainedValuesStore'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 RetainedValuesStore is not retaining 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 RetainedValuesStore is not retaining 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 retaining 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 RetainedValuesStore is retaining 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 RetainedValuesStore is destroyed without its content being restored

  • Be retired if the RetainedValuesStore'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 RetainedValuesStore is not retaining exited values, then the scope will immediately be retired and behave like a RetainedEffect. 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 RetainedValuesStore is not retaining 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 retaining 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 RetainedValuesStore is retaining 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 RetainedValuesStore is destroyed without its content being restored

  • Be retired if the RetainedValuesStore'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 RetainedValuesStore is not retaining 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 RetainedValuesStore is not retaining 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 retaining 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 RetainedValuesStore is retaining 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 RetainedValuesStore is destroyed without its content being restored

  • Be retired if the RetainedValuesStore'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 RetainedValuesStore is not retaining 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 RetainedValuesStore is not retaining 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 retaining 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 RetainedValuesStore. 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 transiently destroyed (later referred to as a "retention scenario") 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 LocalRetainedValuesStore 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 LocalRetainedValuesStore is not retaining 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 RetainedValuesStore
with the same keys └─────────────────┬─────────┘

┌─Yes────────────────┘ value not
restored and
.──────────────────┴──────────────────. store stops
└─▶( isRetainingExitedValues ) retaining 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 RetainedValuesStore. 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 transiently destroyed (later referred to as a "retention scenario") 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 LocalRetainedValuesStore 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 LocalRetainedValuesStore is not retaining 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 RetainedValuesStore
with the same keys └─────────────────┬─────────┘

┌─Yes────────────────┘ value not
restored and
.──────────────────┴──────────────────. store stops
└─▶( isRetainingExitedValues ) retaining 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 RetainedValuesStore 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

retainControlledRetainedValuesStore

@Composable
fun retainControlledRetainedValuesStore(): ControlledRetainedValuesStore

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

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

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

When this value stops being retained, it will automatically stop retaining exited values, regardless of how many times ControlledRetainedValuesStore.startRetainingExitedValues 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.LocalRetainedValuesStore
import androidx.compose.runtime.retain.RetainedContentHost
import androidx.compose.runtime.retain.retain
import androidx.compose.runtime.retain.retainControlledRetainedValuesStore

@Composable
fun AnimatedRetainedContentHost(active: Boolean, content: @Composable () -> Unit) {
    // Create a RetainedValuesStore. It will be added as a child to the current store and start
    // retaining exited values when the parent does. On Android, this store will implicitly
    // survive and forward retention events caused by configuration changes.
    val retainedValuesStore = retainControlledRetainedValuesStore()
    AnimatedContent(active) { targetState ->
        if (targetState) {
            // Install the RetainedValuesStore over the child content
            CompositionLocalProvider(LocalRetainedValuesStore provides retainedValuesStore) {
                // 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
            // retaining
            // 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 RetainedValuesStore's state to the
            // visibility of its content. You can manage the RetainedValuesStore in any way
            // suitable for your content.
            val composer = currentComposer
            DisposableEffect(retainedValuesStore) {
                // Stop retaining 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 (retainedValuesStore.retainExitedValuesRequestsFromSelf > 0) {
                        composer.scheduleFrameEndCallback {
                            retainedValuesStore.stopRetainingExitedValues()
                        }
                    } else {
                        null
                    }

                onDispose {
                    // Start retaining exited values when we deactivate
                    cancellationHandle?.cancel()
                    retainedValuesStore.startRetainingExitedValues()
                }
            }
        }
    }
}
Returns
ControlledRetainedValuesStore

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

retainRetainedValuesStoreRegistry

@Composable
fun retainRetainedValuesStoreRegistry(): RetainedValuesStoreRegistry

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

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

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

Top-level properties

LocalRetainedValuesStore

val LocalRetainedValuesStoreProvidableCompositionLocal<RetainedValuesStore>

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

The default implementation is a ForgetfulRetainedValuesStore that causes retain to behave the same as remember. On Android, a lifecycle-aware RetainedValuesStore 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 store and will follow the new store's retention lifecycle.

RetainedValuesStores 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 RetainedValuesStore starts retaining 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.