How to snap scrollable items to center of screen in Jetpack Compose
Snapper
migrated fromSnapFlingBehavior
Previously, we explored a common use case in Android app development: snapping scrollable items to the center of the screen after a fling gesture.
To achieve this in Jetpack Compose we use the previously archived "Snapper" library. Snapper provides a seamless solution for implementing snapping behavior in scrollable layouts when the official framework does not yet support it. If you missed that article, you can read it here.
Jetpack Compose has grown since then, and the good news is that the functionality we previously implemented using the Snapper library is now officially supported in Jetpack Compose, introducing SnapFlingBehavior
. This new class provides a more comprehensive and efficient solution for centering items in scrollable lists with dynamic content.
In this article, we'll revisit the issues we tackled in previous articles and demonstrate how to transition from using the Snapper library to the officially supported one SnapFlingBehavior
. We'll start by discussing the main differences between the two approaches, and then provide a step-by-step guide for implementing it in a Jetpack Compose project SnapFlingBehavior
.
Convert to SnapFlingBehavior
For a quick recap, let's take a look at the core functionality we implement with Snapper. This library allows us to create a dynamic scrollable list that snaps items to the center of the screen after a fling gesture. When Jetpack Compose doesn't have a feature that officially implements this use case, it's a great solution.
With the introduction SnapFlingBehavior
, we can now implement this snapping behavior using Jetpack Compose's official API. SnapFlingBehavior
Provides greater flexibility and customization options than the Snapper library .
SnapFlingBehavior
Class to make items snap to a given position, using shortSnapVelocityThreshold
a parameter to differentiate between short/scroll snaps and long/fling snaps. In addition, it provides various animation specifications for different situations, such as highVelocityAnimationSpec
, , lowVelocityAnimationSpec
and snapAnimationSpec
.
Now let's see how we can leverage SnapFlingBehavior to achieve the same functionality we achieved earlier using the Snapper library.
Revisiting our previous use cases
In my last article on the Snapper library, we used an example of a dynamic list:
@Composable
fun MainContent(
placeholderItems: List<String>
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
text = "Example Horizontal LazyRow"
)
LazyRow(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
items(items = placeholderItems) {
itemMessage: String ->
PlaceholderCard(itemMessage)
}
}
}
}
The actual effect is as follows:
we can use Snapper to achieve fling behavior, as shown in the following code snippet:
val lazyListState: LazyListState = rememberLazyListState()
val layoutInfo: LazyListSnapperLayoutInfo = rememberLazyListSnapperLayoutInfo(lazyListState)
LazyRow(
modifier = Modifier.fillMaxWidth(),
state = lazyListState,
flingBehavior = rememberSnapperFlingBehavior(lazyListState),
contentPadding = PaddingValues(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
items(items = placeholderItems) {
itemMessage: String ->
PlaceholderCard(itemMessage)
}
}
Use SnapFlingBehavior
However, to ensure that visible items scroll to the center of the screen after performing a fling gesture, we no longer need to rely on external libraries. Instead, we can leverage what was discussed earlier SnapFlingBehavior
.
The following code snippet shows SnapFlingBehavior
enabled MainContent
:
@Composable
fun MainContent(
placeholderItems: List<String>
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
text = "Example Horizontal LazyRow"
)
val listState: LazyListState = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = 32.dp),
verticalAlignment = Alignment.CenterVertically,
state = listState,
flingBehavior = rememberSnapFlingBehavior(listState)
) {
items(placeholderItems) {
item: String ->
PlaceholderCard(itemMessage = item)
}
}
}
}
First, we rememberLazyListState()
create one using functions LazyListState
, which allow us to store and manage our LazyRow
component's state:
val listState: LazyListState = rememberLazyListState()
Next, we defined LazyRow
the component :
modifier = Modifier.fillMaxWidth()
: Make sureLazyRow
that spans the entire width of the screen.contentPadding = PaddingValues(horizontal = 32.dp)
: Applies a padding of 32.dp horizontallyLazyRow
inside the .verticalAlignment = Alignment.CenterVertically
:LazyRow
Centers the items in the vertically.state = listState
:LazyListState
Sets toLazyRow
the state of .flingBehavior = rememberSnapFlingBehavior(listState)
: By providinglistState
,SnapFlingBehavior
assign toLazyRow
so that the items in the list snap to the center after a fling gesture and finally achieve the desired behavior.
The final effect is as follows:
in conclusion
In this article, we revisited the issue of snapping scrollable items to the center of the screen in Jetpack Compose and switched from using the Snapper library to the officially supported one SnapFlingBehavior
.
We first saw how we previously implemented dynamic, scrollable lists that automatically snap items to the center of the screen after a gesture swipe. Subsequently, we discussed the same method using the official solution. In the end, we found that the difference in code length was not huge and the migration process was very simple.