Skip to content

Pager layouts

Maven Central

A library which provides paging layouts for Jetpack Compose. If you've used Android's ViewPager before, it has similar properties.

Warning

The pager layouts are currently experimental and the APIs could change at any time. All of the APIs are marked with the @ExperimentalPagerApi annotation.

HorizontalPager

HorizontalPager is a layout which lays out items in a horizontal row, and allows the user to horizontally swipe between pages.

HorizontalPager demo

The simplest usage looks like the following:

// Display 10 items
val pagerState = rememberPagerState(pageCount = 10)

HorizontalPager(state = pagerState) { 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.

VerticalPager

VerticalPager is very similar to HorizontalPager but items are laid out vertically, and react to vertical swipes:

VerticalPager demo
// Display 10 items
val pagerState = rememberPagerState(pageCount = 10)

VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

Lazy creation

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.

Offscreen Limit

The PagerState API allows the setting of the initialOffscreenLimit, which defines the number of pages that should be retained on either side of the current page. Pages beyond this limit will be removed, and then recreated as needed. This value defaults to 1, but can be increased to enable pre-loading of more content:

val pagerState = rememberPagerState(
    pageCount = 10,
    initialOffscreenLimit = 2,
)

HorizontalPager(state = pagerState) { page ->
    // ...
}

Item scroll effects

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:

Item effects demo

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(state = pagerState) { 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
    }
}

Reacting to page changes

The PagerState.currentPage property is updated whenever the selected page changes. You can use the snapshowFlow function to observe changes in a flow:

LaunchedEffect(pagerState) {
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Selected page has changed...
    }
}

Indicators

We also publish a sibling library called pager-indicators which provides some simple indicator composables for use with HorizontalPager and VerticalPager.

Pager indicators demo

The HorizontalPagerWithIndicatorSample and VerticalPagerWithIndicatorSample show you how to use these.

Integration with Tabs

A common use-case for HorizontalPager is to be used in conjunction with a TabRow or ScrollableTabRow.

HorizontalPager + TabRow

Provided in the pager-indicators library is a modifier which can be used on a tab indicator like so:

val pagerState = rememberPagerState(pageCount = pages.size)

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(state = pagerState) { page ->
    // TODO: page content
}

Usage

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-pager:<version>"

    // If using indicators, also depend on 
    implementation "com.google.accompanist:accompanist-pager-indicators:<version>"
}

Library Snapshots

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.


Contributions

Please contribute! We will gladly review any pull requests. Make sure to read the Contributing page first though.

License

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.