How can I implement a HorizontalPager with endless scrolling using Jetpack Compose?
In Android app development, displaying content in a visually appealing and interactive manner is crucial for providing an excellent user experience. One popular way to achieve this is by using a horizontal pager, which allows users to swipe through a series of items horizontally, similar to flipping through pages in a book. In this article, we will explore how to implement a horizontal pager in Kotlin for Android using the Jetpack Compose framework.
Setting up the Code
To begin, let’s take a look at the following code snippet:
fun ContentView() {
val list = listOf(
R.drawable.daredevil,
R.drawable.batman,
R.drawable.iron_man,
R.drawable.dr_strange,
R.drawable.black_panther,
)
val pagerState = rememberPagerState()
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
contentAlignment = Alignment.Center
) {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
HorizontalPager(
modifier = Modifier.height(250.dp),
pageCount = Int.MAX_VALUE,
pageSpacing = 15.dp,
contentPadding = PaddingValues(horizontal = 40.dp),
state = pagerState
) { index ->
list.getOrNull(
index % (list.size)
)?.let { item ->
BannerItem(image = item)
}
}
}
}
LaunchedEffect(key1 = Unit, block = {
var initPage = Int.MAX_VALUE / 2
while (initPage % list.size != 0) {
initPage++
}
pagerState.scrollToPage(initPage)
})
}
Understanding the Code
Let’s break down the code and understand each component:
1. Data Preparation
The list
variable represents a list of drawable resources. Each drawable resource corresponds to an image that will be displayed within the horizontal pager. In this example, the list includes images of Daredevil, Batman, Iron Man, Doctor Strange, and Black Panther.
2. Pager State Initialization
The pagerState
variable is created using the rememberPagerState()
function. This function is provided by the Jetpack Compose library and is used to retain the state of the horizontal pager across recompositions. It keeps track of the current page and provides methods for scrolling to specific pages.
3. UI Composition
The Box
composable is used as the root container for the horizontal pager. It has a modifier that sets its size to fill the maximum available space and a background color of white.
Within the Box
, we use the CompositionLocalProvider
composable to override the default overscroll configuration. By providing null
as the value for LocalOverscrollConfiguration
, we disable the overscroll effect when reaching the ends of the pager.
4. Horizontal Pager
The HorizontalPager
composable is the core component responsible for displaying the content in a horizontal swipeable manner. It takes several parameters:
modifier
: Sets the height of the pager to250.dp
, providing a fixed height for the pager.pageCount
: Sets the number of pages in the pager. In this case,Int.MAX_VALUE
is used to create a large number of pages to allow for continuous scrolling.pageSpacing
: Specifies the spacing between pages. Here, it is set to15.dp
.contentPadding
: Sets the horizontal padding for the content within each page. It is set to40.dp
.state
: Binds thepagerState
variable to the pager, allowing it to manage the scrolling behavior and keep track of the current page.
Inside the HorizontalPager
, a lambda function is called for each page, passing the index
of the current page as a parameter. Using the index
and the size of the list
, the current drawable resource is retrieved using list.getOrNull(index % list.size)
. If a valid drawable resource is found, it is passed to the BannerItem
composable, which represents the UI for displaying an image banner.
fun BannerItem(image: Int) {
Box(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(10.dp))
.background(Color.Black),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = image),
contentScale = ContentScale.Crop,
contentDescription = "Banner Image"
)
}
}
5. Initial Page Setup
The LaunchedEffect
composable is used to perform a side effect when the component is first launched. In this case, it is used to set the initial page of the pager.
The block
parameter within LaunchedEffect
contains the code that calculates the initial page. It starts with Int.MAX_VALUE / 2
and increments it until the page is divisible by the size of the list
. This ensures that the initial page is a multiple of the list size, allowing for a seamless scrolling experience.
Once the initial page is calculated, pagerState.scrollToPage(initPage)
is called to scroll the pager to the calculated page.
Full Source Code:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ContentView()
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ContentView() {
val list = listOf(
R.drawable.daredevil,
R.drawable.batman,
R.drawable.iron_man,
R.drawable.dr_strange,
R.drawable.black_panther,
)
val pagerState = rememberPagerState()
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
contentAlignment = Alignment.Center
) {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
HorizontalPager(
modifier = Modifier.height(250.dp),
pageCount = Int.MAX_VALUE,
pageSpacing = 15.dp,
contentPadding = PaddingValues(horizontal = 40.dp),
state = pagerState
) { index ->
list.getOrNull(
index % (list.size)
)?.let { item ->
BannerItem(image = item)
}
}
}
}
LaunchedEffect(key1 = Unit, block = {
var initPage = Int.MAX_VALUE / 2
while (initPage % list.size != 0) {
initPage++
}
pagerState.scrollToPage(initPage)
})
}
@Composable
fun BannerItem(image: Int) {
Box(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(10.dp))
.background(Color.Black),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = image),
contentScale = ContentScale.Crop,
contentDescription = "Banner Image"
)
}
}
Conclusion
The code provided demonstrates how to implement a horizontal pager in Kotlin for Android using the Jetpack Compose framework. The HorizontalPager
composable, along with the rememberPagerState()
function, provides a powerful way to manage the state and behavior of the pager.
Check out Git Repo: Endless-Compose-Pager-Example