Jetpack Compose — Curtain Effect
On this article, I’ll show you how to create a Curtain effect with any composables you want.
- A main cell which appears in the default state
- As many cells as you want to be displayed with a folding effect animation
- The full container will always wrap its size according to the content you give it.
How much customization should be available to the user?
First I did a simple requirement analysis.
- As a user, I want to be able to open the Curtain in a manual way, from outside the Composable, or by clicking on it.
- As a user, I want to able to customize the duration of the folding animation the cells;
- As a user, I want to be free to customize the Main Cell and all of the folding cells.
So, by knowing these requirements, I decided what should be the arguments on the Curtain composable.
isOpenedFromOutside: Boolean? = null
If this argument has any other value than null, it means the user is controlling the Curtain from outside of the composable.
foldingDuration: Int = 250
I guess I don’t really need to describe this one.
mainCell: @Composable () -> Unit
Main Cell composable content, which is customizable by the user.
foldCells: List<@Composable () -> Unit>
All of the cells that will be appearing when Curtain is opened, totally customizable by the user.
To animate the cells by opening and closing the Curtain, I had to keep track of the state (Open || Closed). To accomplish this, have used the remember function, which keeps track of a value during recompositions.
The Main cell, is supposed to be shown, whenever the Curtain is “closed”.
This means that Ineed to keep the cells aware of the state of the curtain, which can be Open or Closed.
Curtain Toggle — Non Manual Way
Toggling the Curtain, will be as simple as changing the ‘isOpened’ variable to false or true.
Now, here are some things I needed to take into account.
To avoid any kind of bugs when opening/closing the curtain, I needed to keep track of the animation state. So, the Curtain can only be opened, when it is fully opened, or fully closed.
- The Curtain can only be opened, if the transition is not running.
- If the transition is not running, we start it by toggling the Curtain state ‘isOpened’.
- In order to change back the transition state ‘isTransitionRunning’ to false, we make use of a coroutine scope ‘foldScope’ to delay the change by the folding duration of each cell * cells quantity (total duration of the Curtain effect).
Curtain Toggle — Manual Way
If the user wants to fully control the Curtain state from outside, we simply need to check the argument ‘isOpenedFromOutside’, and if it has some value, we update the curtain state.
As the layout, I have used a Box with a custom Modifier function.
This is used to decide if the Curtain will be clickable or not.
If the argument ‘isOpenedFromOutside’ is not null, it means the Curtain is externally controlled, so the Curtain will not be clickable.
I have decided to change its alpha with an ‘updateTransition’ whenever the Curtain state is changed.
This sets up a Transition, and updates it with the target provided by targetState. When targetState changes, Transition will run all of its child animations towards their target values specified for the new targetState.
- 0f (0%) Alpha if it is closed;
- 1f (100%) Alpha if it opened;
- Has a duration of 100 milliseconds;
- If the Curtain is being opened, no delay will be applied;
- If the Curtain is being closed, it will be delayed by the folding duration of each cell * cells quantity (total duration of the Curtain effect). This means that will appear only when all of the cells have been hidden;
- Applied to the Box Modifier surrounding the Main Cell.
The folded cells are inside a Column composable (vertical orientation)
Now, the most complicated part, each Folded Cell.
To make each folded cell appear:
- We need to calculate each cell max height to control its size;
- We use an ‘updateTransition’ again, to control the rotation, alpha and size values of the cell, depending if the Curtain is opened to closed.
I decided to use the Layout Composable, to have a bigger control over each Cell max height.
After measure the content, the ‘cellMaxHeight’ variable is updated according to the content max height, which will affect the max size animation.
If you enjoyed it, and if this helped you, you can also consider paying me a coffee. :]