Git Product home page Git Product logo

compose-shared-elements's People

Contributors

mxalbert1996 avatar volkansahin45 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

compose-shared-elements's Issues

Nested SharedElements are misplaced in Overlay during transition

Dude, you've done a great job maintaining the original library, thanks for that.
I've been exploring its capabilities and found a bug.

The problem

Consider a following use-case: we have a screen with a list of cards and want to animate a clicked card and some of its children to a new screen.
More generally, we want a transition of a container SharedElement with some other transition of its children SharedElements. I found out that this bug occurs when using a navigation-compose library, specifically for me it is androidx.navigation:navigation-compose:2.4.0-alpha10. Compose versions do not affect this bug, but mine is 1.0.3.
Here's some sample code: NavHost is itself inside of a SharedElementsRoot, switching composables for it.

SharedElementsRoot {
    Surface(
        modifier = Modifier
            .fillMaxSize(),
        color = MaterialTheme.colors.background
    ) {
        val navController = rememberNavController()
        NavHost(
            navController = navController,
            startDestination = "First_screen"
        ) {
            composable(route = "First_screen") {
                LazyColumn(
                    contentPadding = PaddingValues(vertical = 100.dp) // Simulating position in the list
                ) {
                    item {
                        SharedElement(
                            key = "Container",
                            screenKey = "First"
                        ) {
                            SharedCard {
                                navController.navigate("Second_screen")
                            }
                        }
                    }
                }
            }
            composable(route = "Second_screen") {
                SharedElement(
                    key = "Container",
                    screenKey = "Second"
                ) {
                    SharedCardContent {
                        navController.popBackStack()
                    }
                }
            }
        }
    }
}

And here is the code for containers:

@Composable
fun SharedCard(
    onClick: () -> Unit
) {
    Card(
        modifier = Modifier
            .fillMaxWidth(),
        elevation = 2.dp,
        shape = RoundedCornerShape(5.dp),
        onClick = onClick
    ) {
        Column(
            modifier = Modifier
                .padding(
                    top = 10.dp,
                    bottom = 8.dp,
                    start = 15.dp,
                    end = 15.dp
                )
        ) {
            Text(
                text = "Card title",
                style = MaterialTheme.typography.h6
            )

            Spacer(modifier = Modifier.height(10.dp))

            Column(
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {

                SharedElement(
                    key = "author",
                    screenKey = "First"
                ) {
                    Text("Author name")
                }

                SharedElement(
                    key = "subject",
                    screenKey = "First"
                ) {
                    Text("Subject name")
                }
            }
        }
    }
}

@Composable
fun SharedCardContent(
    onClick: () -> Unit
) {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick),
        color = MaterialTheme.colors.surface,
        elevation = 1.dp
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(20.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp)
        ) {
            SharedElement(
                key = "author",
                screenKey = "Second"
            ) {
                Text("Author: Author name")
            }

            SharedElement(
                key = "subject",
                screenKey = "Second"
            ) {
                Text("Subject: Subject name")
            }

        }
    }
}

It looks exactly like we want during transition forwards, but fails to position nested SharedElements during transition backwards:
1

However, if for some reason the whole tree is recomposed after the forwards transition, transition backwards plays as expected:
2
I assume it's because when SharedElementsRoot is recomposed, it considers the Second screen to be the Start state of the transition and vice versa, so the actual backwards transition becomes the forwards one, which explains its failure to execute the actual forwards transition as intended.
It would be super cool if you could help fix this issue. Thanks!

LazyList blinking

When I use Your library with LazyList with not fullscreen element(e.g. custom AlertDialog), when switching back to card, AlertDialog is blinking. File I used:
DeepDive.zip

screen-20220427-161737.mp4

Thanks!

Just wanted to send over a quick note of thanks for updating this library. A contractor who worked on our project used the original mobnetic version, it was a relief to see your updated fork. I hope you'll keep it going!

Update Dependencies & Documents ๐Ÿ“–

Hello ๐Ÿ‘‹
I think this is a reliable library that is still working.
Please update the project dependencies (compose 1.2.1) and write document for the public functions. At first, I did confused.
Recent updates have stabilized the lazyVerticalGrid, so the demo is outdated.

Interrupted animations cause end element to disappear

Hey Albert! I'm running into an issue where elements will disappear if their transitions are interrupted. I haven't put together a demo repo yet but you could reproduce it pretty easily:

TLDR: Create a simple view that has a shared element that animates slowly between three positions. Add a button to toggle the state from 1 -> 2 -> 3 -> repeat. Click to start an animation and then click again to interrupt it. You'll see that it never makes it to the updated target position; instead, it just disappears completely.

I think the expected behavior would be for it to snap to its end position when interrupted. If this was the case, interrupting the transition from position 1 to 2 should make it snap to 2 and then animate there to position 3.

My troubleshooting attempt

I spent a little time trying to fix this in the code but I haven't found a good workaround. It looks like the root of the issue is that this coroutine calls transition.onTransitionFinished() on whichever transition started first -- it never gets called for the second one. So in my example above, if I interrupt the transition from 1->2, the completion of transition 3 will call onTransitionFinished() on transition2 instead of transition3. This then doesn't unhide the correct element here.

A possible start of a fix might involve tracking the active animation. I found that I could get my code to correctly identify when the wrong element was called in onTransitionFinished() by adding val activeAnimations = mutableMapOf<Any, Any>() to SharedElementsRootState, where the key is the element key and value is the active animating screenKey, and then setting it during onElementDisposed() (the start of the next animation) and checking it during onTransitionFinished(). Something like this...

    fun onElementDisposed(elementInfo: SharedElementInfo) {
        activeAnimations.put(elementInfo.key, elementInfo.screenKey)
        choreographer.postCallback(elementInfo) {
            val tracker = getTracker(elementInfo)
            tracker.onElementUnregistered(elementInfo)
            if (tracker.isEmpty) trackers.remove(elementInfo.key)
        }
    }

@Composable
private fun SharedElementTransitionsOverlay(rootState: SharedElementsRootState) {
  // unchanged...
                scope.launch {
                   // unchanged...
                    activeScreenKey = rootState.activeAnimations.get(startElement.info.key)
                    val correctTarget = activeScreenKey == startElement.info.screenKey
                    Timber.tag("wtf").i("firing onTransitionFinished() for startElement ${startElement.info.screenKey}. Is this correct? $correctTarget")
                    transition.onTransitionFinished()
                }
}

But I haven't figured out what to do with this information, everything I've tried breaks it in a new way.

I'd really appreciate your help with this, it breaks my interface in some ways that are complete show-stoppers. Thank you for all your work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.