레시피 유지
이 레시피는 뒤로 스택에 있는 항목의 값을 유지하는 방법을 보여줍니다. 또한 맞춤 NavEntryDecorator를 구현하는 방법을 보여줍니다.
작동 방식
탐색 3에서 retain을 사용하려면 NavEntryDecorator를 정의하고 설치해야 합니다.
이 데코레이터는 대상이 고유한 RetainedValuesStore로 래핑된 RetainedValuesStoreRegistry를 관리합니다.
이 샘플에 구현된 데코레이터는 항목이 백스택에 있는 동안 유지된 값을 유지합니다. 항목이 팝된 후 다시 추가되면 새로운 보관된 값이 적용됩니다.
유지에 관한 자세한 내용은 retain, RetainedValuesStore, RetainedValuesStoreRegistry 문서를 참고하세요.
NavEntryDecorator에 관한 자세한 내용은 공식 문서를 참고하세요.
package com.example.nav3recipes.retain import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.retain.RetainedValuesStore import androidx.compose.runtime.retain.RetainedValuesStoreRegistry import androidx.compose.runtime.retain.retain import androidx.compose.runtime.retain.retainRetainedValuesStoreRegistry import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavEntryDecorator import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig import kotlinx.serialization.Serializable @Serializable private data object RouteA : NavKey @Serializable private data class RouteB(val id: Int) : NavKey class RetainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = rememberNavBackStack(RouteA) NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryDecorators = listOf( rememberRetainedValuesStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Nav3 with retain support") { Text("Retained value: ${retainValue()}") Button(onClick = dropUnlessResumed { backStack.add(RouteB(1)) }) { Text("Click to navigate") } } } entry<RouteB> { key -> ContentBlue("Route id: ${key.id}") { Text("Retained value: ${retainValue()}") Button(onClick = dropUnlessResumed { backStack.add(RouteB(key.id + 1)) }) { Text("Click to navigate") } } } } ) } } @Composable private fun retainValue() = retain { "Retained Value ${retainCount++}" } private companion object { // Counter to track how many values have been retained // Resets on process death. var retainCount = 0 } } /** * Returns a [RetainedValuesStoreNavEntryDecorator] that is remembered across recompositions backed * by [registry]. * * The underlying storage is controlled by the provided [registry]. By default, a new * [RetainedValuesStoreRegistry] is retained at this point in the composition hierarchy and will be * destroyed when the composition is permanently discarded or when the returned decorator is removed * from the composition hierarchy. If you need the backing storage of this decorator to have a * different lifespan, you can manually manage and provide a [RetainedValuesStoreRegistry] with the * intended lifespan. * * @param registry The underlying [RetainedValuesStoreRegistry] used to provide * [RetainedValuesStore] instances to [NavEntries][NavEntry]. This instance should be retained to * properly survive destruction and recreation scenarios. */ @Composable fun <T : Any> rememberRetainedValuesStoreNavEntryDecorator( registry: RetainedValuesStoreRegistry = retainRetainedValuesStoreRegistry() ): RetainedValuesStoreNavEntryDecorator<T> { return remember(registry) { RetainedValuesStoreNavEntryDecorator(registry) } } /** * Provides the content of each [NavEntry] with a dedicated [RetainedValuesStore] so that each nav * entry may retain its own values. * * @param registry The underlying [RetainedValuesStoreRegistry] used to provide * [RetainedValuesStore] instances to [NavEntries][NavEntry]. This instance should be retained to * properly survive destruction and recreation scenarios. */ class RetainedValuesStoreNavEntryDecorator<T : Any>( registry: RetainedValuesStoreRegistry, ) : NavEntryDecorator<T>( onPop = { key -> registry.clearChild(key) }, decorate = { entry -> registry.LocalRetainedValuesStoreProvider(entry.contentKey) { entry.Content() } }, )