Avoid some undesirable shared element transitions
This implementation is terrible, never let id explicitly travel so many places.
First we introduce a global SETFilter which use dsl to explicitly construct a list of Triple (keyGenerator, NavGraphNode, NavGraphNode)
val SETFilter = SETFilter {
val listScreen = listof(GalleryListScreen, FavouritesScreen...)
// Blacklist:
listScreen.permutations { a, b ->
thumbItem between a and b
}
// Whitelist:
listScreen.forEach {
thumbItem between it and GalleryDetailScreen
}
}
Things to be discussed: SETFilter should be a blacklist of transitions should not animated or a whitelist of transitions should animated?
The following part is SETFilter how to work.
thumbItem is a global TransitionItemKeyGenerator.
EhAsyncThumb takes it to generate key and append it at head of SET key, the generated key is the same unless someone reset it.
The important part is we track navController once outside DestinationHost, detect all current NavBackStackEntry changes, and filter the change with the content SETFilter recorded
For whitelist mode if changes is filtered, where the change is not in whitelist, we reset thumbItem key generator(note that the reset must be executed before Composer walk through new Screen), so the key generated for new screen is different from origin one, so no SET, vice versa.
Through the Filter framework we will successfully decouple (something should animates between some Screen explicitly set) and the Screen itself
Through the Filter framework we will successfully decouple (something should animates between some Screen explicitly set) and the Screen itself
Should we? We might want fine-grained control over the transitions, such as only running them when explicitly initiated by user click. That is, no transitions when navigation is initiated by random pick/clipboard URL/etc., as transitions starting from an unexpected position are counterintuitive.
Through the Filter framework we will successfully decouple (something should animates between some Screen explicitly set) and the Screen itself
Should we? We might want fine-grained control over the transitions, such as only running them when explicitly initiated by user click. That is, no transitions when navigation is initiated by random pick/clipboard URL/etc., as transitions starting from an unexpected position are counterintuitive.
Nonetheless explicitly pass the key through Widgets, Screens and Navigations will increase much unnecessary complexity of each Screen, let's design something more declarative.
Screen
└── Lazy(List|Grid)
└── (List|Grid)Item
└── Thumb
We need to distinguish between list/grid items, so the key should be generated on the call site of (List|Grid)Item.
Screen └── Lazy(List|Grid) └── (List|Grid)Item └── ThumbWe need to distinguish between list/grid items, so the key should be generated on the call site of (List|Grid)Item.
I'd like to avoid distinguish them if the cost is increased complexity and unnatural code. If we just want fine-grained control maybe current shared element transition API is not suitable for us, as we just want the one explicitly set animates, that's something imperative(not declarative).
We need to distinguish between list/grid items, so the key should be generated on the call site of (List|Grid)Item.
I'd like to avoid distinguish them if the cost is increased complexity and unnatural code. If we just want fine-grained control maybe current shared element transition API is not suitable for us, as we just want the one explicitly set animates, that's something imperative(not declarative).
The crossfade will glitch if they use the same keys, so we can't really avoid it.
We need to distinguish between list/grid items, so the key should be generated on the call site of (List|Grid)Item.
I'd like to avoid distinguish them if the cost is increased complexity and unnatural code. If we just want fine-grained control maybe current shared element transition API is not suitable for us, as we just want the one explicitly set animates, that's something imperative(not declarative).
The crossfade will glitch if they use the same keys, so we can't really avoid it.
Maybe we just remove the togetherWith inside Crossfade?
We need to distinguish between list/grid items, so the key should be generated on the call site of (List|Grid)Item.
I'd like to avoid distinguish them if the cost is increased complexity and unnatural code. If we just want fine-grained control maybe current shared element transition API is not suitable for us, as we just want the one explicitly set animates, that's something imperative(not declarative).
The crossfade will glitch if they use the same keys, so we can't really avoid it.
Maybe we just remove the
togetherWithinside Crossfade?
It's already removed.
Friendly ping
Friendly ping
I'd just suggest we extract "remove togetherWith inside CrossFade" to another PR and merge it first, I strongly disapprove passing id String across the whole Screen & NavArgs as it will introduce much complexity and maintenance burden.
Friendly ping
I'd just suggest we extract "remove togetherWith inside CrossFade" to another PR and merge it first, I strongly disapprove passing id String across the whole Screen & NavArgs as it will introduce much complexity and maintenance burden.
The problem is, removing togetherWith alone does not work.
It will glitch as long as list and grid items share the same keys.
Basically, these are what we need:
- list and grid items have different keys
- non-detail screens have different keys
Basically, these are what we need:
- list and grid items have different keys
- non-detail screens have different keys
Is it possible to recompose with different shared element transition key before(suspend and await next composition) navigate to other screen / do crossfade transition?
Basically, these are what we need:
- list and grid items have different keys
- non-detail screens have different keys
Is it possible to recompose with different shared element transition key before(suspend and await next composition) navigate to other screen / do crossfade transition?
If so, maybe we can design a graceful framework to handle this.
Basically, these are what we need:
- list and grid items have different keys
- non-detail screens have different keys
Is it possible to recompose with different shared element transition key before(suspend and await next composition) navigate to other screen / do crossfade transition?
That sounds hacky. And how we gonna pass the new key to detail screen?
Basically, these are what we need:
- list and grid items have different keys
- non-detail screens have different keys
Is it possible to recompose with different shared element transition key before(suspend and await next composition) navigate to other screen / do crossfade transition?
That sounds hacky. And how we gonna pass the new key to detail screen?
Let's extract two model. The first model is something like original shared element transition key, UI element have same key means they are similar thus can do shared element transitions, let's call the key contentKey. The second one is connector, connector connects NodeGenerator, so the two NodeGenerators connected by connector will generate same syntheticKey for same contentKey thus can do shared element transitions. We combine these two models to generate key and do SET.
For implementation details, class NodeGenerator() { // Key is the first model's key, means content is the same fun generate(contentKey:String): Node }
class Node( val contentKey: String val syntheticKey: String )
A connector connects two NodeGenerators, the NodeGenerator tracks all active(remembered) nodes generated by itself. When NodeGenerator generate Nodes, it will firstly search from the opposite NodeGenerator connected with connector, if opposite NodeGenerator have a Node that contains the same contentKey, the newly generated Node will use its syntheticKey, otherwise syntheticKey is always unique(maybe implemented with a self-inc atomicInt).
For example, We have a global NodeGenerator for ListScreen val listGenerator: NodeGenerator there's two thumbs in ListScreen thus two Nodes generated Node A: contentKey gid1 syntheticKey uniqueID1 Node B: contentKey gid2 syntheticKey uniqueID2
We switched to grid, as listGenerator is not connected to listGenerator, newly generated two Nodes still have unique synthetic id Node C: contentKey gid1 syntheticKey uniqueID3 Node D: contentKey gid2 syntheticKey uniqueID4 As syntheticKey is different, thus no shared element transitions.
For now we have Node C: contentKey gid1 syntheticKey uniqueID3 Node D: contentKey gid2 syntheticKey uniqueID4
Then we navigate to DetailScreen with gid1. DetailScreen have its global NodeGenerator val detailGenerator: NodeGenerator detailGenerator is connected to listGenerator so newly generated detail header thumb node Node E contentKey gid1 syntheticKey uniqueID3 E and C have same contentKey, obviously. detailGenerator search from opposite -- listGenerator and find C, so E takes C's syntheticKey. Since C and E's syntheticKey is the same, shared element transition works.
That sounds a lot more complicated. You can make another PR if you feel it's necessary.
That sounds a lot more complicated. You can make another PR if you feel it's necessary.
But this can avoid complexity being exponential increased if we want more dedicated shared element transitions. I would like to implement it but I have no environment recently, if this is a release blocker maybe we need to temporarily disable SET as temporarily solution.