33

How to animate BottomSheet content using Jetpack Compose

 3 years ago
source link: https://proandroiddev.com/how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to animate BottomSheet content using Jetpack Compose

Note: Everything in this post related to Compose 1.0.1 and maybe in the future Google will introduce a new API and approaches like Compose MotionLayout šŸ°

Early this year I started a new pet project for listening to random radio from over the world. The UI at the beginning was very simple and clear, just a logo with the station name and necessary controls. The main functionality is integration with Android Auto, but it is a topic for a separate story (just ask in the comments if you are interested in this theme).

0*oiXKbuIZQcqYE1Iz.png?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
First app iteration

Using the application, it became necessary to see user liked stations, other categories separated by genre and country. The idea is to make a Home screen with a list of different information and player which can be shown and hidden on the screen.

0*b8H1im_iRmFe-wg2.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
Final result

Find available solutions

As a normal developer, I started to research existing compose projects to find already implemented functionality.

The official and community samples contain only the UI part and donā€™t include any interaction.

For example, the Spotify UI demo application for me implies a presence player screen, but in source code, itā€™s just a Row with content.

0*1qCYGnSYvSHmM2z2?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
https://github.com/Gurupreet/ComposeCookBook

Another interesting repository I found is a Podcast App. According to the gif looks like what I was looking for.

0*RAE23aE7KJKK5hIA.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
https://github.com/fabirt/podcast-app

After inspecting the source code I understood how it works. The PodcastPlayerScreen is a Root composable for the player and it added to the composition of the main screen. By default, itā€™s not visible for users.

1*Y6e3wnDSivcbPczvPvKlbg.png?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
https://github.com/fabirt/podcast-app/blob/6b4876fd007c54e3f10160c091d3c29147b20211/android/app/src/main/java/com/fabirt/podcastapp/ui/MainActivity.kt#L57

Then if the user clicks on some podcast item it changes the showPlayerFullScreen boolean. Recomposition triggers the AnimatedVisibility logic and the player appears.

1*mI2R-lYKtUuPb9rA5MFJ7A.png?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
https://github.com/fabirt/podcast-app/blob/6b4876fd007c54e3f10160c091d3c29147b20211/android/app/src/main/java/com/fabirt/podcastapp/ui/podcast/PodcastPlayerScreen.kt#L50

Further interaction refers to listening swipe offset and move the player along the Y-axis on the screen.

1*YRQEAlCMLiCPMNvwSJ9X1Q.png?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
https://github.com/fabirt/podcast-app/blob/6b4876fd007c54e3f10160c091d3c29147b20211/android/app/src/main/java/com/fabirt/podcastapp/ui/podcast/PodcastPlayerScreen.kt#L116

From my perspective, it's not an ideal solution, because it is tied to the ViewModel state, the unobvious showPlayerFullScreen variable which can be changed in different places, also we need manually operate with offset, and so on.

Letā€™s stop research and try to make a more simple and clear solution.

BottomSheet story

Out of the box, Compose contains two types of BottomSheet implementations:

To make a simple screen with ModalBottomSheetLayout we just need to initialize rememberModalBottomSheetState with initial Hidden state, create coroutine scope for future interaction with visibility and that's all.

ModalBottomSheetLayout example

Unfortunately, ModalBottomSheetLayout doesnā€™t have the ability to set peekHeight.

peekHeight ā€” he height of the bottom sheet when it is collapsed. Simply speaking bottom sheet will be on the screen permanently, but in the collapsed state.

BottomSheetScaffold is trying to solve this issue and introduce this customization. The simple screen setup is pretty the same, but we are able to set peekHeight as a parameter.

BottomSheetScaffold example

Important note:
In different posts I saw wrong initialization of
rememberBottomSheetScaffoldState without remember block for bottomSheetState.

It can lead unexpected bottomsheet behaviour after recomposition like non responding to touches, able to drag in different direction and so on.

The correct one should be:

Working with BottomSheetScaffold

In the sample project, I have added the demo UI for the radio player with collapsed and expanded state. The main goal now is to animate from one state to another one.

collapsed and enabled state

At the first step, we need to know the current BottomSheet state.

The good news for us is that BottomSheetState inherited from SwipeableState and we can try to access fields currentValue andtargetValue.

BottomSheetState source code

The code will look pretty simple, we just read the necessary data from bottomSheetState and show it on the screen.

0*jjRqpvBSQGriGToF.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
Target and current values

Well, now we know the state of our screen and animation direction. But thatā€™s not enough for animation, we need intermediate values between Collapsed and Expanded states.

Digging through the source code I found that Swipeable storing information about the ongoing swipe or animation in a special progress field.

Swipeable source code

SwipeProgress is a class with from/to parameters and fraction. The fraction represents the current position between from and to.

SwipeProgress source code

Letā€™s use this. Updated DebugScreen will look like this.

0*BkazrCBWnpVH2-Ig.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
DebugSceen with fraction

Looks nice, but unusable now. If you look closely you will see that fraction starts from 0 every time and finishes with 1.

For beautiful animation, we should definitely assign fraction for states:

  • Expanded ā†’ 1f
  • Collapsed ā†’ 0f

A little math and we get this extension.

0*biS_Of5zJKeSf0RH.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
New calculated fraction

Super, this is already 90% of the result. Now we can apply the calculated fraction to any composable function and dynamically change the alpha.

In this code snippet, I change the alpha of the root collapsed element relatively to a fraction.

0*fhRopmkow6TjdAc2.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
Content animation inside BottomSheet

Looks pretty, doesnā€™t it?

Bonus

We can try to animate BottomSheet corner radius relatively to a fraction.

For example, for the Collapsed state, we wonā€™t have any rounding, but for Expanded we want to have 30.dp.

The below code allows achieving dynamic corner radius change.

0*pn2nmfC-ZISlgITb.gif?q=20
how-to-animate-bottomsheet-content-using-jetpack-compose-3eab972b3bdc
Dynamic corner radius

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK