OverView:
This tutorial explains how to create a Horizontal Pager (Similar to Android View system ViewPager ) with page indicators in Jetpack Compose.
Use case:
In this example , I take a use case to display Deals in the form Carousel using Horizontal Pager in Jetpack Compose.
What is Horizontal Pager?
Jetpack Compose HorizontalPager is a composable that allows you to create a horizontally scrollable list of items or pages. It’s commonly used to implement a horizontal carousel or a swipeable view with multiple pages. The HorizontalPager composable is part of the Accompanist library, which provides various utilities for working with animations and interactions in Compose.
Dependencies Required:
The below are the dependencies required to implement Horizontal Pager:
implementation 'androidx.compose.foundation:foundation:1.4.3'
implementation "com.google.accompanist:accompanist-pager-indicators:0.30.1"
HorizontalPager is part of foundation library where as the page indicators that are displayed at the bottom of the carousel are part of accompanist library.
Explanation
The HorizontalPager composable function is below as per documentation:
@Composable
fun HorizontalPager(
pageCount: Int,
modifier: Modifier = Modifier,
state: PagerState = rememberPagerState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
pageSize: PageSize = PageSize.Fill,
beyondBoundsPageCount: Int = 0,
pageSpacing: Dp = 0.dp,
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),
userScrollEnabled: Boolean = true,
reverseLayout: Boolean = false,
key: ((index: Int) -> Any)? = null,
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Horizontal
),
pageContent: @Composable (page: Int) -> Unit
)
Explanation of important parameters:
count
: This is the total number of pages or items in the pager. It represents the number of elements you want to display horizontally.state
: This parameter is of typePagerState
and is used to control the state of the pager. It allows you to get and set the current page, listen to page changes, and perform other interactions with the pager.pageContent
: A lambda that takes anInt
parameter, which represents the current page index, and returns the content to display for that page. Inside this lambda, you can define the composable content for each page based on the provided index.offscreenLimit
: This parameter sets the maximum number of pages that can be pre-created and kept in memory when offscreen. By default, it's set to1
, meaning only one page is pre-created in addition to the currently visible page.dragEnabled
: A boolean parameter that determines whether dragging to swipe between pages is enabled. If set tofalse
, the user won't be able to swipe between pages.reverseLayout
: A boolean parameter that determines the layout direction of the pager. If set totrue
, the pages will be laid out in reverse order.verticalAlignment
: An alignment parameter that controls the vertical alignment of the content inside the pager. The default value isAlignment.Top
.horizontalAlignment
: An alignment parameter that controls the horizontal alignment of the content inside the pager. The default value isAlignment.Start
.pageTransformer
: A transformer function that allows you to apply custom transformations to the pages as they are scrolled or animated. This can be used to create various visual effects during page transitions.pageScroll
: A parameter of typePagerScroll
that allows you to customize how the pager scrolls. You can set the scrolling behavior and other properties related to scrolling.pageFling
: A parameter of typePagerFling
that allows you to customize the fling behavior of the pager. You can set properties related to fling velocity and animation.pageSnapping
: A parameter of typePagerSnapping
that allows you to customize the behavior of snapping pages when they are scrolled. You can set properties related to page snapping.stateWrapper
: A parameter of typePagerStateWrapper
, which allows you to wrap the providedPagerState
in a custom implementation. It can be used to add additional functionality to the pager state.vertical
: A boolean parameter that indicates whether the pager should be vertical or horizontal. If set totrue
, the pager will be vertical; otherwise, it will be horizontal. Note that this is not used inHorizontalPager
, as it's meant for vertical pagers.modifier
: A modifier that allows you to apply styling and layout adjustments to the pager.
HorizontalPagerIndicator
The composable function of HorizontalPagerIndicator is as below :
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HorizontalPagerIndicator(
pagerState: androidx.compose.foundation.pager.PagerState,
pageCount: Int,
modifier: Modifier = Modifier,
pageIndexMapping: (Int) -> Int = { it },
activeColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
inactiveColor: Color = activeColor.copy(ContentAlpha.disabled),
indicatorWidth: Dp = 8.dp,
indicatorHeight: Dp = indicatorWidth,
spacing: Dp = indicatorWidth,
indicatorShape: Shape = CircleShape,
)
modifier
: A modifier that allows you to apply styling and layout adjustments to the HorizontalPagerIndicator.pageCount
: This is the total number of pages in the HorizontalPager to which the indicator corresponds. It represents the number of pages or items you have in the HorizontalPager.pagerState
: This parameter is of typePagerState
and is used to connect the HorizontalPagerIndicator to the state of the HorizontalPager. It allows the indicator to reflect the current page position and update accordingly as the user swipes between pages.activeColor
: The color of the indicator for the currently active page. This color will be applied to the indicator dot that corresponds to the current page.inactiveColor
: The color of the indicator for the inactive pages. This color will be applied to the indicator dots that correspond to pages other than the current page.indicatorWidth
: The width of each indicator dot. You can use this parameter to set the width of the dots.indicatorHeight
: The height of each indicator dot. You can use this parameter to set the height of the dots.indicatorSpacing
: The spacing between each indicator dot. You can use this parameter to add space between the dots.indicatorShape
: The shape of the indicator dot. You can use this parameter to specify a custom shape for the dots. The default shape is Circle.indicatorContainerColor
: The color of the container that holds the indicator dots. You can use this parameter to set the background color of the indicator container.indicatorContainerModifier
: A modifier that allows you to apply styling and layout adjustments to the container that holds the indicator dots.alignment
: An alignment parameter that controls the alignment of the indicator dots within the container. The default value isAlignment.Center
.horizontalArrangement
: A horizontal arrangement parameter that controls the horizontal arrangement of the indicator dots within the container.verticalArrangement
: A vertical arrangement parameter that controls the vertical arrangement of the indicator dots within the container.indicatorContent
: A lambda that allows you to customise the content of each indicator dot. Inside this lambda, you can define the composable content for each indicator dot, such as an icon or a text label.
Implementation :
As discussed above I’m going to display “Deals” similar to shopping app where we use Horizontal Pager to scroll each deal with pager indicator.
The below is my composable function to display deals :
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HorizontalPagerWithIndicators(deals: List<Deal>) {
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
Column{
HorizontalPager(pageCount = deals.size, state = pagerState,
contentPadding = PaddingValues(horizontal = 20.dp), pageSpacing = 10.dp) { page ->
Column() {
DisplayDeal(deal = deals[page])
Box(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(top = 10.dp, bottom = 10.dp)
) {
HorizontalPagerIndicator(
pageCount = deals.size,
pagerState = pagerState,
modifier = Modifier.align(Alignment.Center).clickable {
val currentPage = pagerState.currentPage
val totalPages = deals.size
val nextPage = if (currentPage < totalPages - 1) currentPage + 1 else 0
coroutineScope.launch { pagerState.animateScrollToPage(nextPage) }
}
)
}
}
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.currentPage }
.collect { currentPage ->
pagerState.animateScrollToPage(currentPage)
}
}
}
}
}
The above composable function takes list of deals as argument
The current state of page can be maintained by using rememberPagerState()
We set some of the required parameters of HorizontalPager like pageCount = deals.size, state = pagerState,
contentPadding = PaddingValues(horizontal = 20.dp), pageSpacing = 10.dp. Remaining all values will set to default as per HorizontalPager Composable function.
To display content of each page and Pager indicator , I have used a Column that accommodates content of each page and HorizontalPager Indicator. Similarly in HorizontalPagerIndicator we set only some required parameters ,remaining all will set to default.
There is some logic implemented as part of HorizontalPagerIndicator to display next page when clicked on each indicator.
The “ DisplayDeal(deal = deals[page])” composable function implementation is below:
@Composable
fun DisplayDeal(deal: Deal) {
Box(modifier = Modifier.fillMaxWidth().height(200.dp).background(Color.Red),
contentAlignment = Alignment.Center,
) {
FetchImageFromUrl(imageUrl = deal.category.categoryImage)
Text(
text = deal.dealValue,
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(8.dp)
.background(Color.Black)
.padding(horizontal = 4.dp, vertical = 2.dp)
)
}
}
I am using which occupies whole width and height is set as 200.dp.
An Image of product is displayed and corresponding deal info is displayed at top right of the Box.
@Composable
fun FetchImageFromUrl(imageUrl: String) {
val painter: Painter = rememberAsyncImagePainter(imageUrl)
Image(
painter = painter, contentDescription = null, modifier = Modifier.size(50.dp),
colorFilter = ColorFilter.tint(Color.Blue)
)
}
To know more info on how to fetch image from URL and library used is clearly explained below:
Model classes required for this:
data class Deal(val category: Category,val dealValue:String)
data class Category(val id: UUID = UUID.randomUUID(), val categoryName: String,val categoryImage:String)
Data can be fetched from ViewModel or sample dummy info can be populated to create list and pass to HorizontalPagerWithIndicators() composable function .
Conclusion:
In this tutorial we have learnt how to use HorizontalPager in Jetpack compose with an example.