RetainedValuesStoreRegistry


A RetainedValuesStoreRegistry creates and manages RetainedValuesStore instances for collections of items. This is desirable for components that swap in and out children where each child should be able to retain values when it becomes removed from the composition hierarchy.

To use this class, wrap your content in LocalRetainedValuesStoreProvider with a unique key. Each content block wrapped in this way will receive a unique RetainedValuesStore that retains values when the content is removed from the composition hierarchy. Values are retained for as removed content block until either the registry is disposed or its key is cleared from this registry.

When a RetainedValuesStoreRegistry is no longer used, you must call dispose before the provider is garbage collected. This ensures that all retained values are correctly retired. Failure to do so may result in leaked memory from undispatched RetainObserver.onRetired callbacks. Instances created by RetainedValuesStoreRegistry are automatically disposed when the provider stops being retained.

This registry is intended to be used for managing multiple RetainedValuesStore instances, for child composables that enter and exit the composition at different times and should retain values separately. For example, this component may be used with navigation containers, lists, etc.

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.runtime.retain.LocalRetainedValuesStoreProvider
import androidx.compose.runtime.retain.retain
import androidx.compose.runtime.retain.retainRetainedValuesStoreRegistry
import androidx.compose.ui.graphics.painter.Painter

// List item that retains a value
@Composable
fun Contact(contact: Contact, deleteContact: () -> Unit) {
    Row {
        // Retain this painter to cache the contact icon in memory
        val contactIcon = retain { ContactIconPainter() }
        Image(contactIcon, "Contact icon")
        Text(contact.name)
        // Optional delete action (likely nested in a DropdownMenu)
        IconButton(onClick = deleteContact) { Icon(Icons.Default.Delete, "Delete contact") }
    }
}

@Composable
fun ContactsList(contacts: List<Contact>) {
    // Create the RetainedValuesStoreRegistry
    val retainedValuesStoreRegistry = retainRetainedValuesStoreRegistry()
    LazyColumn {
        items(contacts) { contact ->
            // Install it for an item in a list
            retainedValuesStoreRegistry.LocalRetainedValuesStoreProvider(contact.id) {
                // This contact now gets its own retain store.
                // If the store of ContactsList starts retaining exited values, this nested
                // store will too. If this contact leaves and re-enters composition, it will
                // keep its previously retained values.
                Contact(
                    contact = contact,
                    deleteContact = {
                        // Call into the contacts provider to delete the contact
                        // ...
                        //
                        // Optional: Purge child stores if a contact gets deleted. This is a
                        // "best effort" when a contact is deleted and might not capture all
                        // deletions. If we miss a deletion, we'll continue retaining for that
                        // contact until the list is retired. If this is a large pool of
                        // objects, you may choose to be more aggressive about how you clear
                        // keys from the registry.
                        retainedValuesStoreRegistry.clearChild(contact.id)
                    },
                )
            }
        }
    }
}

Summary

Public constructors

Cmn

Public functions

Unit

Installs child content that should be retained under the given key.

Cmn
Unit
clearChild(key: Any?)

Removes the RetainedValuesStore for the child with the given key from this RetainedValuesStoreRegistry.

Cmn
Unit
clearChildren(predicate: (Any?) -> Boolean)

Bulk removes all child stores for which the predicate returns true.

Cmn
Unit

Removes all child RetainedValuesStores from this RetainedValuesStoreRegistry and marks it as ineligible for future use.

Cmn

Public constructors

RetainedValuesStoreRegistry

RetainedValuesStoreRegistry()

Public functions

LocalRetainedValuesStoreProvider

@Composable
fun LocalRetainedValuesStoreProvider(key: Any?, content: @Composable () -> Unit): Unit

Installs child content that should be retained under the given key. keys must be unique within the composition hierarchy for a given RetainedValuesStoreRegistry.

When removed, this composable retains exited values from the content lambda under the given key. When added back to the composition hierarchy, the underlying store will restore these values and dispose unused values when the composition completes.

This composable only attempts to manage the retention lifecycle for the content and key pair. It will retain removed content indefinitely until clearChild or clearChildren is invoked.

Parameters
key: Any?

The unique child key associated with the given content. This key is used to identify the retention pool for objects retained by the content composable.

content: @Composable () -> Unit

The composable content to compose with the RetainedValuesStore of the given key

Throws
kotlin.IllegalStateException

if dispose has been called

kotlin.IllegalArgumentException

if the same key is used twice in the composition hierarchy under this registry.

clearChild

fun clearChild(key: Any?): Unit

Removes the RetainedValuesStore for the child with the given key from this RetainedValuesStoreRegistry. If the key doesn't have an associated RetainedValuesStore yet (either because it hasn't been created or has already been cleared), this function does nothing.

If the store being cleared is currently retaining exited values, it will stop as a result of this call. If a child with the given key is currently in the composition hierarchy, its retained values will not be persisted the next time the child content is destroyed. Children orphaned this way effectively behave as if their LocalRetainedValuesStore is the ForgetfulRetainedValuesStore until LocalRetainedValuesStoreProvider is recomposed to create a new store.

If LocalRetainedValuesStoreProvider is called again for the given key, a new RetainedValuesStore will be created for the child.

Parameters
key: Any?

The key of the child content whose RetainedValuesStore should be discarded

clearChildren

fun clearChildren(predicate: (Any?) -> Boolean): Unit

Bulk removes all child stores for which the predicate returns true. This function follows the same clearing rules as clearChild.

Parameters
predicate: (Any?) -> Boolean

The predicate to evaluate on all child keys in the RetainedValuesStoreRegistry. If the predicate returns true for a given key, it will be cleared. If the predicate returns false it will remain in the collection.

See also
clearChild

dispose

fun dispose(): Unit

Removes all child RetainedValuesStores from this RetainedValuesStoreRegistry and marks it as ineligible for future use. This is required to invoke when the store is no longer used to retire any retained values. Failing to do so may result in memory leaks from undispatched RetainObserver.onRetired and RetainedEffect callbacks. When this function is called, all values retained in stores managed by this provider will be immediately retired.

If this store has already been disposed, this function will do nothing.