Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screen based animation overrides #1901

Merged
merged 37 commits into from
Mar 1, 2025
Merged

Screen based animation overrides #1901

merged 37 commits into from
Mar 1, 2025

Conversation

stagg
Copy link
Collaborator

@stagg stagg commented Jan 21, 2025

About

Building out a surface where navigation transitions can be overridden at navigation time based on the type of navigation event and the source/target screens.

Due to how shared element transitions work with AnimatedContent, we need some ability to completely replace the the transition spec before the target shared elements are loaded into the composition. Can look at PetDetailAnimatedScreenTransform for an example of this.

AnimatedScreenTransform usage

  • This interface allows you to customize the EnterTransition, z-index, and SizeTransform for a Screen as it becomes the top screen or is replaced by another Screen in the navigation stack.
  • You can also customize the ExitTransition for a screen that is no longer the top screen.
  • Each can be customized based on the AnimatedNavEvent that triggered the transition.

Transition Selection

  1. Get base transform: Calls the transitionSpec on the decorator to obtain a ContentTransform based on the AnimatedNavEvent.
  2. Check for screen-specific overrides: Checks for any AnimatedScreenTransform overrides defined for the target screen and the initial screen.
  3. Combine overrides: It combines the base transform, and screen-specific overrides into a final ContentTransform. Screen-specific overrides take precedence over the base transform.

Example

object PetDetailAnimatedScreenTransform : AnimatedScreenTransform {

  override fun AnimatedContentTransitionScope<AnimatedNavState>.enterTransition(
    animatedNavEvent: AnimatedNavEvent
  ): EnterTransition? {
    // Going to the detail screen
    if (initialState.screen !is HomeScreen) return null
    if (!targetState.screen.isSharedElementDetailScreen()) return null
    return EnterTransition.None
  }

  override fun AnimatedContentTransitionScope<AnimatedNavState>.exitTransition(
    animatedNavEvent: AnimatedNavEvent
  ): ExitTransition? {
    // Going back to the home screen
    if (targetState.screen !is HomeScreen) return null
    if (!initialState.screen.isSharedElementDetailScreen()) return null
    return ExitTransition.None
  }
}

The example checks if the user is navigating to the PetDetailScreen from the HomeScreen, with the information needed to tie together a shared element transition. If that is the case no transitions are used, otherwise it returns null to fallback to the default nav decorator transition.

Demo

Notice how on the forward navigation the grid view is sliding out, on the pop AndroidPredictiveBackNavDecorator already removes the animation.

Before After
before.mp4
after.mp4

@stagg stagg marked this pull request as ready for review February 11, 2025 19:14
@stagg stagg requested a review from ZacSweers February 11, 2025 19:14
Copy link
Collaborator

@ZacSweers ZacSweers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good! A few meta-level things before you merge

  • Let's stick circuit's experimental annotations on these new APIs
  • Let's add some bits to the CHANGELOG and site docs

And one for a later PR - what do you think of adding a shared element tutorial to the inbox tutorial? I think having it in the STAR sample is great as a reference but I wonder if it'd also be good to have a tutorial that walks one through writing one in a simpler case like that.

import kotlinx.collections.immutable.ImmutableMap

/**
* Animated navigation decoration is an implementation of [NavDecoration] that provides the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before you merge, let's maybe add a simple sample snippet to this kdoc as a reference?

Comment on lines +76 to +77
// todo DI this
.addAnimatedScreenTransforms(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you wanna handle this in this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planning on doing so separately, given the size of this PR

Comment on lines +281 to +282
screen: KClass<out Screen>,
animatedNavigationTransform: AnimatedScreenTransform,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always kind of envisioned this as taking two keys (current and destination), do you think that's not necessary with this API? Example case: maybe photo viewer can do a hero element transition from a pet detail to the photo viewer but not if it's from a deeplink from home?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think having the single screen for a general screen override of any transition is valuable. Inside that you can check against the initial/target states to set specific screen to screen transitions.
The API right now is definitely more verbose, so I could see a need to add some sugar on top of this to make it easier to do simpler transitions.

@stagg stagg added this pull request to the merge queue Mar 1, 2025
Merged via the queue into main with commit 68c7f44 Mar 1, 2025
5 checks passed
@stagg stagg deleted the j-screen-animation-overrides branch March 1, 2025 00:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants