From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose

In my recent musings about revamping the home screen of my open-source TMDB movie listing app using Compose, an exciting concept struck me — why not integrate a captivating carousel to showcase upcoming movies? In this article, I will share my journey …


This content originally appeared on Level Up Coding - Medium and was authored by Somesh Kumar

In my recent musings about revamping the home screen of my open-source TMDB movie listing app using Compose, an exciting concept struck me — why not integrate a captivating carousel to showcase upcoming movies? In this article, I will share my journey of developing and implementing a custom carousel with Compose, providing insights into the different steps involved.

At the outset, I searched for a pre-existing solution, considering the availability of a carousel component in the Material Design 3 documentation. However, I soon discovered that it hadn’t been released for Jetpack Compose and was only accessible in the Alpha version of the latest MDC-Android (View system). This led me to make the decision to develop my own carousel, utilizing the HorizontalPager, a Compose component that allows for horizontal item scrolling and page transformation.

To tackle the development process effectively, I divided the problem into four distinct parts:

  1. Transforming the HorizontalPager to achieve the desired carousel effect.
  2. Incorporating indicators to display the currently visible page.
  3. Implementing automatic scrolling for the HorizontalPager pages.
  4. Utilizing animation to enhance the appearance of upcoming pages, while focusing on the currently displayed page and fading out the others.

Let’s dive into each of these parts in detail.

Transforming the HorizontalPager to achieve the desired carousel effect.

To create the desired carousel effect, we must first customize the HorizontalPager to display the previous and next pages partially.
For this purpose, the HorizontalPager offers two parameters:

  1. contentPadding: Allows us to apply margins from each axis, creating the desired spacing around the pages.
  2. pageSpacing: Introduces a gap between two adjacent pages, ensuring a visually appealing layout.
HorizontalPager(
contentPadding = PaddingValues(horizontal = 32.dp),
pageSpacing = 16.dp
) { page ->
...
CarouselItem(item)
}

Incorporating indicators to display the currently visible page

This task was straightforward to accomplish. I simply needed to place the HorizontalPager within a Box layout and align the DotIndicators composable to the bottom of the Box.

  Box {
HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
CarouselItem(itemList[page])
}

DotIndicators(
pageCount = pageCount,
pagerState = pagerState,
modifier = Modifier.align(Alignment.BottomCenter)
)
}

We created a composable called DotIndicators. It takes two parameters: pageCountand pageState. We use pageCount to add the same number of indicators, and we use pageState to make the selected dot appear darker. The inner implementation looks like this:

@Composable
fun DotIndicators(
...,
modifier: Modifier
) {
Row(modifier = modifier) {
repeat(pageCount) { iteration ->
val color = if (pagerState.currentPage == iteration) selectedColor else unselectedColor
Box(
modifier = Modifier
.clip(CircleShape)
.background(color)
)
}
}
}

This is what it looks like with the dot indicator

Implementing automatic scrolling for the HorizontalPager pages

Currently, our setup requires users to manually swipe horizontally to navigate to the next page. To enhance the user experience, we can implement automatic scrolling. We achieve this by leveraging Coroutine, LaunchedEffect, and PagerState provided by HorizontalPager.
Here’s an example of a full-function implementation:

@Composable
fun AwesomeCarousel(
pageCount: Int = 10,
pagerState: PagerState = rememberPagerState(),
autoScrollDuration: Long = 3000L
) {
val isDragged by pagerState.interactionSource.collectIsDraggedAsState()
if (isDragged.not()) {
with(pagerState) {
var currentPageKey by remember { mutableStateOf(0) }
LaunchedEffect(key1 = currentPageKey) {
launch {
delay(timeMillis = autoScrollDuration)
val nextPage = (currentPage + 1).mod(pageCount)
animateScrollToPage(page = nextPage)
currentPageKey = nextPage
}
}
}
}

HorizontalPager(pageCount = pageCount, state = pagerState) { page ->
CarouselItem(itemList[page])
}
}

The main takeaways from the code snippet are:

  1. LaunchedEffect: The LaunchedEffect is a special effect provided by Jetpack Compose that allows us to perform side effects in a coroutine when a specific key value changes. In this case, the key1 parameter is set to currentPageKey, which means the effect will be triggered whenever the value of currentPageKey changes. Inside the LaunchedEffect block, we launch a new coroutine using the launch function. This coroutine is executed asynchronously and independently from the composition process.
  2. delay(timeMillis = autoScrollDuration): This line introduces a delay in the coroutine for a specified duration indicated by the autoScrollDuration variable. It pauses the execution of the coroutine for the specified time before proceeding to the next line and after the pause, we change the key again which results in the relaunch of the coroutine so basically it means that the block inside the LaunchedEffect will keep on executing infinite times.
  3. isDragged.not()This code segment ensures that the subsequent operations are only performed if the pager is not being dragged. It provides control over the automatic scrolling behavior, preventing conflicts between user interaction and the automatic scrolling process For example, if the user is in the middle of swiping, the auto-scrolling will stop.

Enhancing Page Appearance with Animation

The final part of the implementation focuses on enhancing the visual transition effect for the carousel pages. By utilizing the graphicLayer modifier, we can achieve smooth and seamless transitions. The key animations involve fading out the outgoing item while fading in the incoming item, along with scaling the items as they approach or move away from the center.

The following code calculates an offset between the current page and the target page, then uses this offset to interpolate a transformation value between 0.7and 1. This transformation value is then applied to both the alpha and scaleY properties of a graphicLayer

fun Modifier.carouselTransition(page: Int, pagerState: PagerState) =
graphicsLayer {
val pageOffset =
((pagerState.currentPage - page) + pagerState.currentPageOffsetFraction).absoluteValue

val transformation =
lerp(
start = 0.7f,
stop = 1f,
fraction = 1f - pageOffset.coerceIn(0f, 1f)
)
alpha = transformation
scaleY = transformation
}
  1. We calculate the offset between the current page and the target page in a pager. It takes into account both the whole page difference (pagerState.currentPage - page) and the fraction (pagerState.currentPageOffsetFraction) that represents how far the transition has progressed between pages. The absolute value ensures that the offset is always positive.
  2. The lerp function is used to interpolate a value between a start and stop point based on a fraction. In this case, the start value is 0.7f, the stop value is 1f, and the fraction is calculated as 1f - pageOffset.coerceIn(0f, 1f). The coerceIn function ensures that the fraction stays within the range of 0 to 1.
  3. alpha = transformationThis assigns the value of transformation to the alpha property of the graphicLayer. Setting alpha to a value between 0 and 1 determines the transparency of the layer. A value of 1 means the layer is fully opaque, while 0 means it is completely transparent.
  4. scaleY = transformationSimilarly, this line assigns the value of transformation to the scaleY property of the graphicLayer. Adjusting the scaleY property scales the layer vertically. A value of 1 means the layer is at its original size, while values less than 1 reduce the vertical scale, and values greater than 1 increase it.

we utilize the graphicLayer extension function on the item modifier of HorizontalPager. This allows us to apply transformations and create effects for each carousel item.

HorizontalPager() { page ->
val page = list[page]
CarouselItem(
item = page,
modifier = Modifier.carouselTransition(page, pagerState)
)
}

Here is how this carousel looks in slow motion

For a complete implementation of this Carousel, you can explore the open-source project on GitHub:

GitHub - TheSomeshKumar/Flickophile: Jetpack Compose project showcasing Android MVVM guideline published on https://developer.android.com/topic/architecture

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job


From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Somesh Kumar


Print Share Comment Cite Upload Translate Updates
APA

Somesh Kumar | Sciencx (2023-05-21T19:06:11+00:00) From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose. Retrieved from https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/

MLA
" » From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose." Somesh Kumar | Sciencx - Sunday May 21, 2023, https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/
HARVARD
Somesh Kumar | Sciencx Sunday May 21, 2023 » From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose., viewed ,<https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/>
VANCOUVER
Somesh Kumar | Sciencx - » From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/
CHICAGO
" » From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose." Somesh Kumar | Sciencx - Accessed . https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/
IEEE
" » From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose." Somesh Kumar | Sciencx [Online]. Available: https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/. [Accessed: ]
rf:citation
» From Idea to Implementation: Developing an Animated Carousel with Jetpack Compose | Somesh Kumar | Sciencx | https://www.scien.cx/2023/05/21/from-idea-to-implementation-developing-an-animated-carousel-with-jetpack-compose/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.