androidx.compose.foundation.pager

Interfaces

PageInfo

This represents a single measured page in a Pager layout.

Cmn
PageSize

This is used to determine how Pages are laid out in Pager.

Cmn
PagerLayoutInfo

Contains useful information about the currently displayed layout state of a Pager.

Cmn
PagerScope

Receiver scope for Pager.

Cmn
PagerSnapDistance

PagerSnapDistance defines the way the Pager will treat the distance between the current page and the page where it will settle.

Cmn

Classes

PageSize.Fixed

Multiple pages in a viewport

Cmn
PagerState

The state that can be used to control VerticalPager and HorizontalPager

Cmn

Objects

PageSize.Fill

Pages take up the whole Pager size.

Cmn
PagerDefaults

Contains the default values used by Pager.

Cmn

Top-level functions summary

Unit
@Composable
HorizontalPager(
    state: PagerState,
    modifier: Modifier,
    contentPadding: PaddingValues,
    pageSize: PageSize,
    beyondViewportPageCount: Int,
    pageSpacing: Dp,
    verticalAlignment: Alignment.Vertical,
    flingBehavior: TargetedFlingBehavior,
    userScrollEnabled: Boolean,
    reverseLayout: Boolean,
    key: ((index: Int) -> Any)?,
    pageNestedScrollConnection: NestedScrollConnection,
    snapPosition: SnapPosition,
    pageContent: @Composable PagerScope.(page: Int) -> Unit
)

A Pager that scrolls horizontally.

Cmn
LazyLayoutScrollScope

A LazyLayoutScrollScope that allows customization of animated scroll in Pager.

Cmn
PagerState
PagerState(
    currentPage: Int,
    currentPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float,
    pageCount: () -> Int
)

Creates a default PagerState to be used with a Pager

Cmn
Unit
@Composable
VerticalPager(
    state: PagerState,
    modifier: Modifier,
    contentPadding: PaddingValues,
    pageSize: PageSize,
    beyondViewportPageCount: Int,
    pageSpacing: Dp,
    horizontalAlignment: Alignment.Horizontal,
    flingBehavior: TargetedFlingBehavior,
    userScrollEnabled: Boolean,
    reverseLayout: Boolean,
    key: ((index: Int) -> Any)?,
    pageNestedScrollConnection: NestedScrollConnection,
    snapPosition: SnapPosition,
    pageContent: @Composable PagerScope.(page: Int) -> Unit
)

A Pager that scrolls vertically.

Cmn
PagerState
@Composable
rememberPagerState(
    initialPage: Int,
    initialPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float,
    pageCount: () -> Int
)

Creates and remember a PagerState to be used with a Pager

Cmn

Top-level functions

HorizontalPager

@Composable
fun HorizontalPager(
    state: PagerState,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    pageSize: PageSize = PageSize.Fill,
    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
    pageSpacing: Dp = 0.dp,
    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
    userScrollEnabled: Boolean = true,
    reverseLayout: Boolean = false,
    key: ((index: Int) -> Any)? = null,
    pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(state, Orientation.Horizontal),
    snapPosition: SnapPosition = SnapPosition.Start,
    pageContent: @Composable PagerScope.(page: Int) -> Unit
): Unit

A Pager that scrolls horizontally. Pages are lazily placed in accordance to the available viewport size. By definition, pages in a Pager have the same size, defined by pageSize and use a snap animation (provided by flingBehavior to scroll pages into a specific position). You can use beyondViewportPageCount to place more pages before and after the visible pages.

If you need snapping with pages of different size, you can use a snapFlingBehavior with a SnapLayoutInfoProvider adapted to a LazyList.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// Creates a 1-pager/viewport horizontal pager with single page snapping
val state = rememberPagerState { 10 }
HorizontalPager(
    state = state,
    modifier = Modifier.fillMaxSize(),
) { page ->
    Box(
        modifier =
            Modifier.padding(10.dp).background(Color.Blue).fillMaxWidth().aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(text = page.toString(), fontSize = 32.sp)
    }
}
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp

// This is a sample using NestedScroll and Pager.
// We use the toolbar offset changing example from
// androidx.compose.ui.samples.NestedScrollConnectionSample

val pagerState = rememberPagerState { 10 }

val toolbarHeight = 48.dp
val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
val nestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
            val delta = available.y
            val newOffset = toolbarOffsetHeightPx.value + delta
            toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
            return Offset.Zero
        }
    }
}

Box(modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection)) {
    TopAppBar(
        modifier =
            Modifier.height(toolbarHeight).offset {
                IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt())
            },
        title = { Text("Toolbar offset is ${toolbarOffsetHeightPx.value}") }
    )

    val paddingOffset =
        toolbarHeight + with(LocalDensity.current) { toolbarOffsetHeightPx.value.toDp() }

    HorizontalPager(
        modifier = Modifier.fillMaxSize(),
        state = pagerState,
        contentPadding = PaddingValues(top = paddingOffset)
    ) {
        Column(modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
            repeat(20) {
                Box(
                    modifier =
                        Modifier.fillMaxWidth()
                            .height(64.dp)
                            .padding(4.dp)
                            .background(if (it % 2 == 0) Color.Black else Color.Yellow),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = it.toString(),
                        color = if (it % 2 != 0) Color.Black else Color.Yellow
                    )
                }
            }
        }
    }
}
Parameters
state: PagerState

The state to control this pager

modifier: Modifier = Modifier

A modifier instance to be applied to this Pager outer layout

contentPadding: PaddingValues = PaddingValues(0.dp)

a padding around the whole content. This will add padding for the content after it has been clipped, which is not possible via modifier param. You can use it to add a padding before the first page or after the last one. Use pageSpacing to add spacing between the pages.

pageSize: PageSize = PageSize.Fill

Use this to change how the pages will look like inside this pager.

beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount

Pages to compose and layout before and after the list of visible pages. Note: Be aware that using a large value for beyondViewportPageCount will cause a lot of pages to be composed, measured and placed which will defeat the purpose of using lazy loading. This should be used as an optimization to pre-load a couple of pages before and after the visible ones. This does not include the pages automatically composed and laid out by the pre-fetcher in the direction of the scroll during scroll events.

pageSpacing: Dp = 0.dp

The amount of space to be used to separate the pages in this Pager

verticalAlignment: Alignment.Vertical = Alignment.CenterVertically

How pages are aligned vertically in this Pager.

flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state)

The TargetedFlingBehavior to be used for post scroll gestures.

userScrollEnabled: Boolean = true

whether the scrolling via the user gestures or accessibility actions is allowed. You can still scroll programmatically using PagerState.scroll even when it is disabled.

reverseLayout: Boolean = false

reverse the direction of scrolling and layout.

key: ((index: Int) -> Any)? = null

a stable and unique key representing the item. When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one. If null is passed the position in the list will represent the key.

pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(state, Orientation.Horizontal)

A NestedScrollConnection that dictates how this Pager behaves with nested lists. The default behavior will see Pager to consume all nested deltas.

snapPosition: SnapPosition = SnapPosition.Start

The calculation of how this Pager will perform snapping of pages. Use this to provide different settling to different positions in the layout. This is used by Pager as a way to calculate PagerState.currentPage, currentPage is the page closest to the snap position in the layout (e.g. if the snap position is the start of the layout, then currentPage will be the page closest to that).

pageContent: @Composable PagerScope.(page: Int) -> Unit

This Pager's page Composable.

See also
SnapLayoutInfoProvider

for the implementation of a SnapLayoutInfoProvider that uses androidx.compose.foundation.lazy.LazyListState.

Please refer to the samples to learn how to use this API.

LazyLayoutScrollScope

fun LazyLayoutScrollScope(state: PagerState, scrollScope: ScrollScope): LazyLayoutScrollScope

A LazyLayoutScrollScope that allows customization of animated scroll in Pager. The scope contains information about the layout where animated scroll can be performed as well as the necessary tools to do that respecting the scroll mutation priority.

import androidx.compose.animation.core.animate
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.layout.LazyLayoutScrollScope
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.LazyLayoutScrollScope
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

suspend fun PagerState.customScroll(block: suspend LazyLayoutScrollScope.() -> Unit) = scroll {
    block.invoke(LazyLayoutScrollScope(this@customScroll, this))
}

val itemsList = (0..100).toList()
val state = rememberPagerState { itemsList.size }
val scope = rememberCoroutineScope()

Column(Modifier.verticalScroll(rememberScrollState())) {
    Button(
        onClick = {
            scope.launch {
                state.customScroll {
                    snapToItem(40, 0) // teleport to item 40
                    val distance = calculateDistanceTo(50).toFloat()
                    var previousValue = 0f
                    androidx.compose.animation.core.animate(
                        0f,
                        distance,
                        animationSpec = tween(5_000)
                    ) { currentValue, _ ->
                        previousValue += scrollBy(currentValue - previousValue)
                    }
                }
            }
        }
    ) {
        Text("Scroll To Item 50")
    }

    HorizontalPager(state) {
        Box(Modifier.padding(2.dp).background(Color.Red).height(600.dp).fillMaxWidth()) {
            Text(itemsList[it].toString())
        }
    }
}
Parameters
state: PagerState

The PagerState associated with the layout where this animated scroll should be performed.

scrollScope: ScrollScope

The base ScrollScope where the scroll session was created.

Returns
LazyLayoutScrollScope

An implementation of LazyLayoutScrollScope that works with HorizontalPager and VerticalPager.

PagerState

fun PagerState(
    currentPage: Int = 0,
    currentPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float = 0.0f,
    pageCount: () -> Int
): PagerState

Creates a default PagerState to be used with a Pager

Please refer to the sample to learn how to use this API.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// You can use PagerState to define an initial page
val state = rememberPagerState(initialPage = 5) { 10 }
HorizontalPager(modifier = Modifier.fillMaxSize(), state = state) { page ->
    Box(
        modifier =
            Modifier.padding(10.dp).background(Color.Blue).fillMaxWidth().aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(text = page.toString(), fontSize = 32.sp)
    }
}
Parameters
currentPage: Int = 0

The pager that should be shown first.

currentPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float = 0.0f

The offset of the initial page as a fraction of the page size. This should vary between -0.5 and 0.5 and indicates how to offset the initial page from the snapped position.

pageCount: () -> Int

The amount of pages this Pager will have.

@Composable
fun VerticalPager(
    state: PagerState,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    pageSize: PageSize = PageSize.Fill,
    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
    pageSpacing: Dp = 0.dp,
    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
    userScrollEnabled: Boolean = true,
    reverseLayout: Boolean = false,
    key: ((index: Int) -> Any)? = null,
    pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(state, Orientation.Vertical),
    snapPosition: SnapPosition = SnapPosition.Start,
    pageContent: @Composable PagerScope.(page: Int) -> Unit
): Unit

A Pager that scrolls vertically. Pages are lazily placed in accordance to the available viewport size. By definition, pages in a Pager have the same size, defined by pageSize and use a snap animation (provided by flingBehavior to scroll pages into a specific position). You can use beyondViewportPageCount to place more pages before and after the visible pages.

If you need snapping with pages of different size, you can use a snapFlingBehavior with a SnapLayoutInfoProvider adapted to a LazyList.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// Creates a 1-pager/viewport vertical pager with single page snapping
val state = rememberPagerState { 10 }
VerticalPager(state = state, modifier = Modifier.fillMaxSize()) { page ->
    Box(
        modifier =
            Modifier.padding(10.dp).background(Color.Blue).fillMaxWidth().aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(text = page.toString(), fontSize = 32.sp)
    }
}
Parameters
state: PagerState

The state to control this pager

modifier: Modifier = Modifier

A modifier instance to be apply to this Pager outer layout

contentPadding: PaddingValues = PaddingValues(0.dp)

a padding around the whole content. This will add padding for the content after it has been clipped, which is not possible via modifier param. You can use it to add a padding before the first page or after the last one. Use pageSpacing to add spacing between the pages.

pageSize: PageSize = PageSize.Fill

Use this to change how the pages will look like inside this pager.

beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount

Pages to compose and layout before and after the list of visible pages. Note: Be aware that using a large value for beyondViewportPageCount will cause a lot of pages to be composed, measured and placed which will defeat the purpose of using lazy loading. This should be used as an optimization to pre-load a couple of pages before and after the visible ones. This does not include the pages automatically composed and laid out by the pre-fetcher in * the direction of the scroll during scroll events.

pageSpacing: Dp = 0.dp

The amount of space to be used to separate the pages in this Pager

horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally

How pages are aligned horizontally in this Pager.

flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state)

The TargetedFlingBehavior to be used for post scroll gestures.

userScrollEnabled: Boolean = true

whether the scrolling via the user gestures or accessibility actions is allowed. You can still scroll programmatically using PagerState.scroll even when it is disabled.

reverseLayout: Boolean = false

reverse the direction of scrolling and layout.

key: ((index: Int) -> Any)? = null

a stable and unique key representing the item. When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one. If null is passed the position in the list will represent the key.

pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(state, Orientation.Vertical)

A NestedScrollConnection that dictates how this Pager behaves with nested lists. The default behavior will see Pager to consume all nested deltas.

snapPosition: SnapPosition = SnapPosition.Start

The calculation of how this Pager will perform snapping of Pages. Use this to provide different settling to different positions in the layout. This is used by Pager as a way to calculate PagerState.currentPage, currentPage is the page closest to the snap position in the layout (e.g. if the snap position is the start of the layout, then currentPage will be the page closest to that).

pageContent: @Composable PagerScope.(page: Int) -> Unit

This Pager's page Composable.

See also
SnapLayoutInfoProvider

for the implementation of a SnapLayoutInfoProvider that uses androidx.compose.foundation.lazy.LazyListState.

Please refer to the sample to learn how to use this API.

rememberPagerState

@Composable
fun rememberPagerState(
    initialPage: Int = 0,
    initialPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float = 0.0f,
    pageCount: () -> Int
): PagerState

Creates and remember a PagerState to be used with a Pager

Please refer to the sample to learn how to use this API.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// You can use PagerState to define an initial page
val state = rememberPagerState(initialPage = 5) { 10 }
HorizontalPager(modifier = Modifier.fillMaxSize(), state = state) { page ->
    Box(
        modifier =
            Modifier.padding(10.dp).background(Color.Blue).fillMaxWidth().aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(text = page.toString(), fontSize = 32.sp)
    }
}
Parameters
initialPage: Int = 0

The pager that should be shown first.

initialPageOffsetFraction: @FloatRange(from = -0.5, to = 0.5) Float = 0.0f

The offset of the initial page as a fraction of the page size. This should vary between -0.5 and 0.5 and indicates how to offset the initial page from the snapped position.

pageCount: () -> Int

The amount of pages this Pager will have.