Blog Infos
Author
Published
Topics
Published
There is a lot behind what you see!

In this tutorial, I’ll show you how can you create an user stack animation as shown below

Well, if you look at this animation it seems like a vertical list of user pictures collapses into a horizontal list and all gets overlapped with each other. We’ve seen such stacking on Jira dashboards 😄.

Can we use Row and Column?

My first thought actually was of converting Column into Rows and vice versa but immediately realisation hit and it seems like a bad idea because then we would have to manage shifting the items from column to row along with animation and visibilities and I’m not sure how easy it would be.

Hence that approach was dropped.

Stack approach

The actual approach used in this is stacking all the items one over the other first and then altering their offsets as per their position and put them in the right location on screen.

Let’s consider if the item is of size 50dp, then on the vertical arrangement, the items are placed simply with top margin equal to their index multiplied with 50dp as shown in the diagram.

Calculation for vertical alignment

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

Coding the animation

Let’s begin by setting up the vertical arrangement of items. Simply take a box and and for each user calculate its offsetX and offsetY as show above in the diagram.

val userImages = listOf(
R.drawable.img_user_1,
R.drawable.img_user_2,
R.drawable.img_user_3,
R.drawable.img_user_4,
R.drawable.img_user_5,
)
...
Box(modifier = Modifier.fillMaxSize()) {
userImages.forEachIndexed { index, drawable ->
val offsetX = (screenWidth - 25.dp) / 2 // horizontal offset
val offsetY = ((screenHeight - 25.dp) / 2) + (index * 50).dp // vertical offset including item index
val modifier = Modifier
.size(50.dp)
.absoluteOffset(
x = offsetX,
y = offsetY,
)
UserImage(modifier = modifier, image = drawable)
}
FloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
onClick = { isHorizontal = isHorizontal.not() }
) {
Text("Click")
}
}
view raw BoxLogic.kt hosted with ❤ by GitHub

This will give us our vertical arrangement. Now lets settle our horizontal arrangement by tweaking the offsets calculation as follows

var isHorizontal by remember {
     mutableStateOf(false)
}
...

val offsetX = (screenWidth - 25.dp) / 2 + if (isHorizontal) (index * 25).dp else 0.dp
val offsetY = ((screenHeight  - 25.dp) / 2) + if (isHorizontal) 0.dp else (index * 50).dp

...

What we did here was created a isHorizontal variable which toggles when we click on the button to change the arrangement.

We added screen width and height portion in calculation just to bring the view into centre and it is not necessary.

For vertical arrangement, we increase the y coordinate by index times 50dp.

For horizontal arrangement, we increase the x coordinate by index times 25dp (50dp % 2).

By far, what we get it as below

Cool. But what is missing here is the smooth transition from one arrangement to the other. So let’s add that too. In order to do that we need to make our offsets to transition from their last value to the new value. Also setting these new offsets in our modifier as follows

// new offsetX
val targetCordX by animateDpAsState(
    targetValue = offsetX,
    animationSpec = tween(
        easing = easing,
        durationMillis = animationDuration
    )
)
// new offsetY
val targetCordY by animateDpAsState(
    targetValue = offsetY,
    animationSpec = tween(
        easing = easing,
        durationMillis = animationDuration
    )
)

val modifier = Modifier
                .size(50.dp)
                .absoluteOffset(
                    x = targetCordX,  // offsetX -> targetCordX
                    y = targetCordY,  // offsetY -> targetCordY
                )

We simply animate our offsets as dp with new offsets values every time the arrangement is toggled. Now the result looks like this:

Whoa! We can see it is working as expected. Only one part is yet to be done. The last item in the stack hides for horizontal arrangement and shows up again for vertical and that too with an animation. So let’s do that too.

To make the last item hide/show, we simply add the condition that if the arrangement is horizontal and index is the last one then hide it otherwise show it.

val lastElementVisibility = if (isHorizontal && index == userImages.lastIndex) 0f else 1f

Now we create the alpha variable which will animate our visibility across the transitions as follows

val alpha by animateFloatAsState(
                targetValue = lastElementVisibility,
                animationSpec = tween(
                    easing = easing,
                    durationMillis = animationDuration
                )
            )

val modifier = Modifier
                .size(50.dp)
                .absoluteOffset(
                    x = targetCordX,
                    y = targetCordY,
                )
                .alpha(alpha)  // affects the visibility along the transition

And bam! We successfully created our expected user stack animation.

You can clone the repo from github and play around with different scenarios.

GitHub – aqua30/UserStackAnimation: Sample project to show how can we create user stack animation.

Credits

The reference for this animation is taken from LinkedIn where it is created in flutter.

That is all for now! Stay tuned!

Connect with me (if the content is helpful to you ) on

Until next time…

Cheers!

 

This article was previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu