A library which provides paging layouts for Jetpack Compose. If you‘ve used Android’s ViewPager
before, it has similar properties.
!!! warning This library is deprecated, with official pager support in androidx.compose.foundation.pager. The original documentation is below the migration guide.
androidx.compose.foundation.pager
.com.google.accompanist.pager.HorizontalPager
to androidx.compose.foundation.pager.HorizontalPager
, and the same for com.google.accompanist.pager.VerticalPager
to change to androidx.compose.foundation.pager.VerticalPager
count
variable to pageCount
.itemSpacing
parameter to pageSpacing
.rememberPagerState()
to androidx.compose.foundation.pager.rememberPagerState()
One thing to note is that there is a new parameter on androidx.compose.foundation.Pager
, for pageSize
, by default this uses a PageSize.Fill
, but can also be changed to use a fixed size, like PageSize.Fixed(200.dp)
for a fixed size paging.
The following is a mapping of the pager classes from accompanist to androidx.compose:
accompanist/pager | androidx.compose.foundation |
---|---|
HorizontalPager | androidx.compose.foundation.pager.HorizontalPager |
VerticalPager | androidx.compose.foundation.pager.VerticalPager |
rememberPagerState | androidx.compose.foundation.pager.rememberPagerState |
PagerState#pageCount | Use canScrollForward or canScrollBackward |
calculateCurrentOffsetForPage | Use (pagerState.currentPage - page) + pagerState.currentPageOffsetFraction |
PagerState#currentPageOffset | PagerState#currentPageOffsetFraction |
Modifier.pagerTabIndicatorOffset() | Implement it yourself, or still include and use accompanist-pager-indicators , it now supports androidx.compose.foundation.pager.PagerState |
HorizontalPagerIndicator | Implement it yourself, or fork accompanist-pager-indicators implementation |
VerticalPagerIndicator | Implement it yourself, or fork accompanist-pager-indicators implementation |
PagerDefaults.flingBehavior() | androidx.compose.foundation.pager.PagerDefaults.flingBehavior() |
The biggest change is that HorizontalPager
and VerticalPager
's number of pages is now called pageCount
instead of count
.
The following is the deprecated guide for using Pager in Accompanist. Please see above migration section for how to use the androidx.compose
Pager.
HorizontalPager
is a layout which lays out items in a horizontal row, and allows the user to horizontally swipe between pages.
The simplest usage looks like the following:
// Display 10 items HorizontalPager(count = 10) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
If you want to jump to a specific page, you either call call pagerState.scrollToPage(index)
or pagerState.animateScrollToPage(index)
method in a CoroutineScope
.
val pagerState = rememberPagerState() HorizontalPager(count = 10, state = pagerState) { page -> // ...page content } // Later, scroll to page 2 scope.launch { pagerState.scrollToPage(2) }
VerticalPager
is very similar to HorizontalPager
but items are laid out vertically, and react to vertical swipes:
// Display 10 items VerticalPager(count = 10) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
Pages in both HorizontalPager
and VerticalPager
are lazily composed and laid-out as required by the layout. As the user scrolls through pages, any pages which are no longer required are removed from the content.
Under the covers, HorizontalPager
use LazyRow
, and VerticalPager
uses LazyColumn
.
HorizontalPager
and VerticalPager
both support the setting of content padding, which allows you to influence the maximum size and alignment of pages.
You can see how different content padding values affect a HorizontalPager
below:
=== “start = 64.dp”
Setting the start padding has the effect of aligning the pages towards the end. {: loading=lazy width=70% align=center } ``` kotlin HorizontalPager( count = 4, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content } ```
=== “horizontal = 32.dp”
Setting both the start and end padding to the same value has the effect of centering the item horizontally. {: loading=lazy width=70% align=center } ``` kotlin HorizontalPager( count = 4, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content } ```
=== “end = 64.dp”
Setting the end padding has the effect of aligning the pages towards the start. {: loading=lazy width=70% align=center } ``` kotlin HorizontalPager( count = 4, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content } ```
Similar effects for VerticalPager
can be achieved by setting the top
and bottom
values. The value 32.dp
is only used here as an example, you can set each of the padding dimensions to whatever value you wish.
A common use-case is to apply effects to your pager items, using the scroll position to drive those effects.
The HorizontalPagerTransitionSample demonstrates how this can be done:
The scope provided to your pager content allows apps to easily reference the currentPage
and currentPageOffset
. The effects can then be calculated using those values. We provide the calculateCurrentOffsetForPage()
extension functions to support calculation of the ‘offset’ for a given page:
import com.google.accompanist.pager.calculateCurrentOffsetForPage HorizontalPager(count = 4) { page -> Card( Modifier .graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue // We animate the scaleX + scaleY, between 85% and 100% lerp( start = 0.85f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ).also { scale -> scaleX = scale scaleY = scale } // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
The PagerState.currentPage
property is updated whenever the selected page changes. You can use the snapshotFlow
function to observe changes in a flow:
val pagerState = rememberPagerState() LaunchedEffect(pagerState) { // Collect from the pager state a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> AnalyticsService.sendPageSelectedEvent(page) } } VerticalPager( count = 10, state = pagerState, ) { page -> Text(text = "Page: $page") }
We also publish a sibling library called pager-indicators
which provides some simple indicator composables for use with HorizontalPager
and VerticalPager
.
The HorizontalPagerWithIndicatorSample and VerticalPagerWithIndicatorSample show you how to use these.
A common use-case for HorizontalPager
is to be used in conjunction with a TabRow
or ScrollableTabRow
.
Provided in the pager-indicators
library is a modifier which can be used on a tab indicator like so:
val pagerState = rememberPagerState() TabRow( // Our selected tab is our current page selectedTabIndex = pagerState.currentPage, // Override the indicator, using the provided pagerTabIndicatorOffset modifier indicator = { tabPositions -> TabRowDefaults.Indicator( Modifier.pagerTabIndicatorOffset(pagerState, tabPositions) ) } ) { // Add tabs for all of our pages pages.forEachIndexed { index, title -> Tab( text = { Text(title) }, selected = pagerState.currentPage == index, onClick = { /* TODO */ }, ) } } HorizontalPager( count = pages.size, state = pagerState, ) { page -> // TODO: page content }
In v0.19.0 both HorizontalPager
and VerticalPager
were re-written to be based on LazyRow
and LazyColumn
respectively. As part of this change, a number of feature and API changes were made:
pageCount
parameter on rememberPagerState()
has been removed, replaced with the count
parameter on HorizontalPager()
and VerticalPager()
.animationSpec
, initialVelocity
and skipPages
parameters on animateScrollToPage()
have been removed. The lazy components handle this automatically.contentPadding
(see above).key
for each page.horizontalAlignment
parameter on HorizontalPager
, and the verticalAlignment
parameter on VerticalPager
have been removed. A similar effect can be implemented with an appropriate content padding (see above).infiniteLooping
parameter and feature have been removed. A sample demonstrating how to achieve this effect can be found here.offscreenLimit
parameter has been removed. We no longer have control of what items are laid out ‘off screen’.dragEnabled
parameter has removed.PagerScope
(the page item scope) no longer implements BoxScope
.repositories { mavenCentral() } dependencies { implementation "com.google.accompanist:accompanist-pager:<version>" // If using indicators, also depend on implementation "com.google.accompanist:accompanist-pager-indicators:<version>" }
Snapshots of the current development version of this library are available, which track the latest commit. See here for more information on how to use them.
Please contribute! We will gladly review any pull requests. Make sure to read the Contributing page first though.
Copyright 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.