collectAsStateWithLifecycle

A composable reads a StateFlow with collectAsStateWithLifecycle() from androidx.lifecycle:lifecycle-runtime-compose (2.11.0). It returns Compose 234 State, so reading it recomposes on every new value, and it collects only while the lifecycle is at least STARTED. The plain collectAsState() keeps collecting in the background. BookNest's catalog destination is now a "route" composable that connects the ViewModel to the stateless list:

ui/CatalogRoute.kt: the screen reads state and sends events (excerpt)Kotlin
fun SharedTransitionScope.CatalogRoute(
  visibility: AnimatedVisibilityScope,
  onOpen: (Book) -> Unit,
  vm: CatalogViewModel = viewModel(factory = CatalogViewModel.Factory),  // one per entry
) {
  val books by vm.books.collectAsStateWithLifecycle()   // stops collecting below STARTED
  val query by vm.query.collectAsStateWithLifecycle()
  ...
    when (val state = books) {
      LoadState.Loading -> Box(Modifier.fillMaxSize(), Alignment.Center) {
        CircularProgressIndicator()
      }
      ...
      is LoadState.Success -> CatalogList(state.data, visibility, onOpen)
    }

Above the when, an OutlinedTextField shows query and sends vm::onQueryChange; the Error branch shows the message and a Retry button that calls vm::refresh.

viewModel() called inside a composable<Catalog> { } destination scopes the ViewModel to that back-stack entry, so it lives while the catalog is on the stack and is cleared when the entry is popped. Going back to the catalog, pressing Home, waiting ten seconds and returning logged:

Output of 134
D/BookNestNav: now at com.example.booknest.ui.nav.Catalog
D/BookNestVM: books: first collector arrived
D/BookNestVM: books: upstream stopped
D/BookNestVM: books: first collector arrived

In the background the collector stopped, and five seconds later WhileSubscribed(5_000) stopped the upstream combine. On return it restarted and delivered the cached StateFlow value at once, with no reload, because the loaded books live in the ViewModel, not in the flow.