Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Use Circuit's Retained state for back stack support #1551

Merged
merged 3 commits into from
Sep 21, 2023
Merged

Conversation

chrisbanes
Copy link
Owner

@chrisbanes chrisbanes commented Sep 20, 2023

Current

Note: the content changing size and/or wrong scroll position when popping back

Current.mp4

Retained

The content stays stable whilst popping back

Retained.mp4

Future

Still need to work out how (or if) I should get this working with Paging, but that can wait.

github-merge-queue bot pushed a commit to slackhq/circuit that referenced this pull request Sep 20, 2023
…the back stack (#888)

This PR contains a bunch of subtle changes, to enable retained state to
be retained whilst UIs and Presenters are on the back stack. To see this
in action, you can see this [draft
PR](chrisbanes/tivi#1551) on Tivi. The videos
attached to it highlight the benefits pretty clearly 🎉.

This follows on from a big discussion on Slack about the best way to
keep state around so that presenter cold starts don't mean that we're
showing empty states when coming back from the back stack.

## So what are the changes?

### Nested RetainedStateRegistry

Currently `RetainedStateRegistry` is designed to be used single-y,
having a global registry used at the root of the content. Since we need
to manage the lifecycle of retained state differently per back stack
entry, we need a way to be able to 'save' retained state at different
times. This PR implements this by making `RetainedStateRegistry`
nested-aware, such that registries practically become a tree structure.
In practice, this just means a small change to `RetainedStateRegistry`
so as `saveAll()` is proxied to any child registries.

### CanRetainChecker changes

We use `CanRetainChecker` as the way to control the lifecycle of
retained state. This PR changes the default checker to either
`CanRetainChecker.Always` (on non-Android platforms), or makes it nested
registry aware on Android.

### collectAsRetainedState

I found this super useful in Tivi. I initially thought about retaining
the `Presenter`s last emitted `CircuitUiState` (a bit like
`rememberUpdatedState`), but the UI state typically contains some
transient properties (`isLoading`, `errorMessage`, etc) which don't make
sense to retain. Using `rememberRetainedState` or
`collectAsRetainedState` allows retaining of the more long-lived
properties only.

### Other

- `continuityRetainedStateRegistry()`. Moved this to `commonMain` with
simple implementations for non-Android platforms.
- `SaveableHolder` -> `RetainableHolder`
- Updated Star sample to use a `continuityRetainedStateRegistry`

## Future

There's a few things that this PR doesn't tackle:

- Retaining state for different navigation roots. If the user switches
the root navigation entry (i.e via a bottom navigation bar), it would be
nice to (optionally) retain the state of each root, similar to AndroidX
Navigations `saveState` flag. Should be fairly easy, just need to update
the `CanRetainChecker` to retain `if screen was root` (or something).

---------

Co-authored-by: Zac Sweers <pandanomic@gmail.com>
@chrisbanes chrisbanes changed the title [WIP] Use Circuit's Retained state Use Circuit's Retained state for back stack support Sep 21, 2023
@chrisbanes chrisbanes marked this pull request as ready for review September 21, 2023 06:56
@chrisbanes chrisbanes enabled auto-merge (squash) September 21, 2023 06:57
@chrisbanes chrisbanes force-pushed the cb/retained branch 2 times, most recently from 27b1a74 to 6a514a9 Compare September 21, 2023 07:30
@chrisbanes chrisbanes merged commit acb3ae8 into main Sep 21, 2023
@chrisbanes chrisbanes deleted the cb/retained branch September 21, 2023 08:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant