Para pasar el contenido de izquierda a derecha o de arriba abajo, puedes usar los elementos componibles HorizontalPager
y VerticalPager
, respectivamente. Estos elementos componibles tienen funciones similares a ViewPager
en el sistema de vistas. De forma predeterminada, HorizontalPager
ocupa todo el ancho de la pantalla, VerticalPager
ocupa toda la altura y los paginadores solo envían una página a la vez. Todos estos valores predeterminados se pueden configurar.
HorizontalPager
Para crear un selector de páginas que se desplace horizontalmente hacia la izquierda y la derecha, usa HorizontalPager
:
HorizontalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
Para crear un paginador que se desplace hacia arriba y hacia abajo, usa VerticalPager
:
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
Creación diferida
Las páginas de HorizontalPager
y VerticalPager
se componen de forma diferida y se diseñan cuando es necesario. A medida que el usuario se desplaza por las páginas, el elemento componible quita las páginas que ya no son necesarias.
Cargar más páginas fuera de la pantalla
De forma predeterminada, el paginador solo carga las páginas visibles en la pantalla. Para cargar más páginas fuera de la pantalla, establece beyondBoundsPageCount
en un valor superior a cero.
Desplázate hasta un elemento en el paginador
Para desplazarte a una página determinada en el paginador, crea un objeto PagerState
con rememberPagerState()
y pásalo como el parámetro state
al paginador. Puedes llamar a
PagerState#scrollToPage()
en este estado, dentro de un CoroutineScope
:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
Si quieres animar la página, usa la función PagerState#animateScrollToPage()
:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
Cómo recibir notificaciones sobre los cambios de estado de la página
PagerState
tiene tres propiedades con información sobre las páginas: currentPage
, settledPage
y targetPage
.
currentPage
: Es la página más cercana a la posición de ajuste. De forma predeterminada, la posición de ajuste está al comienzo del diseño.settledPage
: Es el número de página cuando no se ejecuta ninguna animación ni desplazamiento. Esto es diferente de la propiedadcurrentPage
, ya quecurrentPage
se actualiza de inmediato si la página está lo suficientemente cerca de la posición de ajuste, perosettledPage
permanece igual hasta que se terminan de ejecutar todas las animaciones.targetPage
: Es la posición de detención propuesta para un movimiento de desplazamiento.
Puedes usar la función snapshotFlow
para observar los cambios en estas variables
y reaccionar a ellos. Por ejemplo, para enviar un evento de Analytics en cada cambio de página, puedes hacer lo siguiente:
val pagerState = rememberPagerState(pageCount = { 10 }) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> // Do something with each page change, for example: // viewModel.sendPageSelectedEvent(page) Log.d("Page change", "Page changed to $page") } } VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
Cómo agregar un indicador de página
Para agregar un indicador a una página, usa el objeto PagerState
para obtener información sobre qué página se seleccionó de la cantidad de páginas y dibuja tu indicador personalizado.
Por ejemplo, si deseas un indicador de círculo simple, puedes repetir la cantidad de círculos y cambiar el color del círculo según si la página está seleccionada con pagerState.currentPage
:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> // Our page content Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }

Aplica efectos de desplazamiento de elementos al contenido
Un caso de uso común es usar la posición de desplazamiento para aplicar efectos a los elementos del paginador. Para saber qué tan lejos está una página de la página seleccionada actualmente, puedes usar PagerState.currentPageOffsetFraction
.
Luego, puedes aplicar efectos de transformación a tu contenido según la distancia de la página seleccionada.
Por ejemplo, para ajustar la opacidad de los elementos según la distancia que se encuentran del centro, cambia el alpha
con Modifier.graphicsLayer
en un elemento dentro del selector de páginas:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager(state = pagerState) { page -> Card( Modifier .size(200.dp) .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 = ( (pagerState.currentPage - page) + pagerState .currentPageOffsetFraction ).absoluteValue // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
Tamaños de página personalizados
De forma predeterminada, HorizontalPager
y VerticalPager
ocupan el ancho o la altura completos, respectivamente. Puedes configurar la variable pageSize
para que tenga un Fixed
, un Fill
(predeterminado) o un cálculo de tamaño personalizado.
Por ejemplo, para establecer una página de ancho fijo de 100.dp
, haz lo siguiente:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Para ajustar el tamaño de las páginas según el tamaño del viewport, usa un cálculo personalizado del tamaño de la página. Crea un objeto PageSize
personalizado y divide el availableSpace
entre tres, teniendo en cuenta el espaciado entre los elementos:
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
Padding del contenido
HorizontalPager
y VerticalPager
admiten el cambio del padding del contenido,
lo que te permite influir en el tamaño máximo y la alineación de las páginas.
Por ejemplo, configurar el padding start
alinea las páginas hacia el final:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
Si configuras el padding de start
y end
en el mismo valor, el elemento se centra horizontalmente:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
Si configuras el padding end
, las páginas se alinean al principio:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
Puedes establecer los valores top
y bottom
para lograr efectos similares para VerticalPager
. El valor 32.dp
solo se usa aquí como ejemplo. Puedes configurar cada una de las dimensiones de padding en cualquier valor.
Personaliza el comportamiento de desplazamiento
Los elementos componibles predeterminados HorizontalPager
y VerticalPager
especifican cómo funcionan los gestos de desplazamiento con el selector de páginas. Sin embargo, puedes personalizar y cambiar los valores predeterminados, como pagerSnapDistance
o flingBehavior
.
Distancia de ajuste
De forma predeterminada, HorizontalPager
y VerticalPager
establecen la cantidad máxima de páginas que un gesto de deslizamiento puede desplazar hasta una página a la vez. Para cambiar esto, establece pagerSnapDistance
en flingBehavior
:
val pagerState = rememberPagerState(pageCount = { 10 }) val fling = PagerDefaults.flingBehavior( state = pagerState, pagerSnapDistance = PagerSnapDistance.atMost(10) ) Column(modifier = Modifier.fillMaxSize()) { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
Crea un paginador con avance automático
En esta sección, se describe cómo crear un selector de páginas con avance automático con indicadores de página en Compose. La colección de elementos se desplaza horizontalmente automáticamente, pero los usuarios también pueden deslizarse manualmente entre los elementos. Si un usuario interactúa con el paginador, se detiene la progresión automática.
Ejemplo básico
En conjunto, los siguientes fragmentos crean una implementación básica de paginador con avance automático con un indicador visual, en el que cada página se renderiza como un color diferente:
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
Puntos clave sobre el código
- La función
AutoAdvancePager
crea una vista de paginación horizontal con avance automático. Toma una lista de objetosColor
como entrada, que se usan como colores de fondo para cada página. pagerState
se crea conrememberPagerState
, que contiene el estado del selector de páginas.pagerIsDragged
ypageIsPressed
hacen un seguimiento de la interacción del usuario.LaunchedEffect
avanza automáticamente el navegador cada dos segundos, a menos que el usuario arrastre el navegador o presione una de las páginas.HorizontalPager
muestra una lista de páginas, cada una con un elemento componibleText
que muestra el número de página. El modificador completa la página, establece el color de fondo depageItems
y hace que se pueda hacer clic en la página.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
Puntos clave sobre el código
- Se usa un elemento componible
Box
como elemento raíz.- Dentro de
Box
, un elementoRow
componible organiza los indicadores de página horizontalmente.
- Dentro de
- Un indicador de página personalizado se muestra como una fila de círculos, en la que cada
Box
recortado a uncircle
representa una página. - El círculo de la página actual tiene el color
DarkGray
, mientras que los otros círculos sonLightGray
. El parámetrocurrentPageIndex
determina qué círculo se renderiza en gris oscuro.
Resultado
En este video, se muestra el paginador básico con avance automático de los fragmentos anteriores: