1# Pager layouts 2 3[](https://search.maven.org/search?q=g:com.google.accompanist) 4 5A library which provides paging layouts for Jetpack Compose. If you've used Android's [`ViewPager`](https://developer.android.com/reference/kotlin/androidx/viewpager/widget/ViewPager) before, it has similar properties. 6 7!!! warning 8 **This library is deprecated, with official pager support in [androidx.compose.foundation.pager](https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/package-summary).** The original documentation is below the migration guide. 9 10## Migration 11 121. Make sure you are using Compose 1.4.0+ before attempting to migrate to `androidx.compose.foundation.pager`. 132. Change `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` 143. Change `count` variable to `pageCount`. 154. Change `itemSpacing` parameter to `pageSpacing`. 165. Change any usages of `rememberPagerState()` to `androidx.compose.foundation.pager.rememberPagerState()` 176. For more mappings - see the migration table below. 187. Run your changes on device and check to see if there are any differences. 19 20One thing to note is that there is a new parameter on `androidx.compose.foundation.Pager`, for `pageSize`, by default this 21uses a `PageSize.Fill`, but can also be changed to use a fixed size, like `PageSize.Fixed(200.dp)` for a fixed size paging. 22 23 24## Migration Table 25 26The following is a mapping of the pager classes from accompanist to androidx.compose: 27 28| accompanist/pager | androidx.compose.foundation | 29|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| 30| `HorizontalPager` | `androidx.compose.foundation.pager.HorizontalPager` | 31| `VerticalPager` | `androidx.compose.foundation.pager.VerticalPager` | 32| `rememberPagerState` | `androidx.compose.foundation.pager.rememberPagerState` | 33| `PagerState#pageCount` | Use `canScrollForward` or `canScrollBackward` | 34| `calculateCurrentOffsetForPage` | Use `(pagerState.currentPage - page) + pagerState.currentPageOffsetFraction` | 35| `PagerState#currentPageOffset` | `PagerState#currentPageOffsetFraction` | 36| `Modifier.pagerTabIndicatorOffset()` | Implement it yourself, or still include and use `accompanist-pager-indicators`, it now supports `androidx.compose.foundation.pager.PagerState` | 37| `HorizontalPagerIndicator` | Implement it yourself, or fork `accompanist-pager-indicators` implementation | 38| `VerticalPagerIndicator` | Implement it yourself, or fork `accompanist-pager-indicators` implementation | 39| `PagerDefaults.flingBehavior()` | `androidx.compose.foundation.pager.PagerDefaults.flingBehavior()` | 40 41The biggest change is that `HorizontalPager` and `VerticalPager`'s number of pages is now called `pageCount` instead of `count`. 42 43# Deprecated Guidance for Accompanist Pager 44The following is the deprecated guide for using Pager in Accompanist. Please see above migration section for how to use the `androidx.compose` Pager. 45 46## HorizontalPager 47 48[`HorizontalPager`][api-horizpager] is a layout which lays out items in a horizontal row, and allows the user to horizontally swipe between pages. 49 50<figure> 51 <video width="300" controls loop> 52 <source src="horiz_demo.mp4" type="video/mp4"> 53 Your browser does not support the video tag. 54 </video> 55 <figcaption>HorizontalPager demo</figcaption> 56</figure> 57 58The simplest usage looks like the following: 59 60``` kotlin 61// Display 10 items 62HorizontalPager(count = 10) { page -> 63 // Our page content 64 Text( 65 text = "Page: $page", 66 modifier = Modifier.fillMaxWidth() 67 ) 68} 69``` 70 71If you want to jump to a specific page, you either call call `pagerState.scrollToPage(index)` or `pagerState.animateScrollToPage(index)` method in a `CoroutineScope`. 72 73``` kotlin 74val pagerState = rememberPagerState() 75 76HorizontalPager(count = 10, state = pagerState) { page -> 77 // ...page content 78} 79 80// Later, scroll to page 2 81scope.launch { 82 pagerState.scrollToPage(2) 83} 84``` 85 86## VerticalPager 87 88[`VerticalPager`][api-vertpager] is very similar to [`HorizontalPager`][api-horizpager] but items are laid out vertically, and react to vertical swipes: 89 90<figure> 91 <video width="300" controls loop> 92 <source src="vert_demo.mp4" type="video/mp4"> 93 Your browser does not support the video tag. 94 </video> 95 <figcaption>VerticalPager demo</figcaption> 96</figure> 97 98``` kotlin 99// Display 10 items 100VerticalPager(count = 10) { page -> 101 // Our page content 102 Text( 103 text = "Page: $page", 104 modifier = Modifier.fillMaxWidth() 105 ) 106} 107``` 108 109## Lazy creation 110 111Pages in both [`HorizontalPager`][api-horizpager] and [`VerticalPager`][api-vertpager] 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. 112 113Under the covers, `HorizontalPager` use [`LazyRow`](https://developer.android.com/jetpack/compose/lists#lazy), and `VerticalPager` uses [`LazyColumn`](https://developer.android.com/jetpack/compose/lists#lazy). 114 115 116## Content Padding 117 118`HorizontalPager` and `VerticalPager` both support the setting of content padding, which allows you to influence the maximum size and alignment of pages. 119 120You can see how different content padding values affect a `HorizontalPager` below: 121 122=== "start = 64.dp" 123 124 Setting the start padding has the effect of aligning the pages towards the end. 125 126 {: loading=lazy width=70% align=center } 127 128 ``` kotlin 129 HorizontalPager( 130 count = 4, 131 contentPadding = PaddingValues(start = 64.dp), 132 ) { page -> 133 // page content 134 } 135 ``` 136 137=== "horizontal = 32.dp" 138 139 Setting both the start and end padding to the same value has the effect of centering the item horizontally. 140 141 {: loading=lazy width=70% align=center } 142 143 ``` kotlin 144 HorizontalPager( 145 count = 4, 146 contentPadding = PaddingValues(horizontal = 32.dp), 147 ) { page -> 148 // page content 149 } 150 ``` 151 152=== "end = 64.dp" 153 154 Setting the end padding has the effect of aligning the pages towards the start. 155 156 {: loading=lazy width=70% align=center } 157 158 ``` kotlin 159 HorizontalPager( 160 count = 4, 161 contentPadding = PaddingValues(end = 64.dp), 162 ) { page -> 163 // page content 164 } 165 ``` 166 167Similar effects for `VerticalPager` can be achieved by setting the `top` and `bottom` values. The value `32.dp` is only used 168here as an example, you can set each of the padding dimensions to whatever value you wish. 169 170## Item scroll effects 171 172A common use-case is to apply effects to your pager items, using the scroll position to drive those effects. 173 174The [HorizontalPagerTransitionSample](https://github.com/google/accompanist/blob/main/sample/src/main/java/com/google/accompanist/sample/pager/HorizontalPagerTransitionSample.kt) demonstrates how this can be done: 175 176<figure> 177 <video width="300" controls loop> 178 <source src="transition_demo.mp4" type="video/mp4"> 179 Your browser does not support the video tag. 180 </video> 181 <figcaption>Item effects demo</figcaption> 182</figure> 183 184 185The scope provided to your pager content allows apps to easily reference the [`currentPage`][currentpage-api] and [`currentPageOffset`][currentpageoffset-api]. The effects can then be calculated using those values. We provide the [`calculateCurrentOffsetForPage()`][calcoffsetpage] extension functions to support calculation of the 'offset' for a given page: 186 187``` kotlin 188import com.google.accompanist.pager.calculateCurrentOffsetForPage 189 190HorizontalPager(count = 4) { page -> 191 Card( 192 Modifier 193 .graphicsLayer { 194 // Calculate the absolute offset for the current page from the 195 // scroll position. We use the absolute value which allows us to mirror 196 // any effects for both directions 197 val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue 198 199 // We animate the scaleX + scaleY, between 85% and 100% 200 lerp( 201 start = 0.85f, 202 stop = 1f, 203 fraction = 1f - pageOffset.coerceIn(0f, 1f) 204 ).also { scale -> 205 scaleX = scale 206 scaleY = scale 207 } 208 209 // We animate the alpha, between 50% and 100% 210 alpha = lerp( 211 start = 0.5f, 212 stop = 1f, 213 fraction = 1f - pageOffset.coerceIn(0f, 1f) 214 ) 215 } 216 ) { 217 // Card content 218 } 219} 220``` 221 222## Reacting to page changes 223 224The [`PagerState.currentPage`][currentpage-api] property is updated whenever the selected page changes. You can use the `snapshotFlow` function to observe changes in a flow: 225 226``` kotlin 227val pagerState = rememberPagerState() 228 229LaunchedEffect(pagerState) { 230 // Collect from the pager state a snapshotFlow reading the currentPage 231 snapshotFlow { pagerState.currentPage }.collect { page -> 232 AnalyticsService.sendPageSelectedEvent(page) 233 } 234} 235 236VerticalPager( 237 count = 10, 238 state = pagerState, 239) { page -> 240 Text(text = "Page: $page") 241} 242``` 243 244## Indicators 245 246We also publish a sibling library called `pager-indicators` which provides some simple indicator composables for use with [`HorizontalPager`][api-horizpager] and [`VerticalPager`][api-vertpager]. 247 248<figure> 249 <video width="300" controls loop> 250 <source src="indicators_demo.mp4" type="video/mp4"> 251 Your browser does not support the video tag. 252 </video> 253 <figcaption>Pager indicators demo</figcaption> 254</figure> 255 256The [HorizontalPagerWithIndicatorSample](https://github.com/google/accompanist/blob/main/sample/src/main/java/com/google/accompanist/sample/pager/HorizontalPagerWithIndicatorSample.kt) and [VerticalPagerWithIndicatorSample](https://github.com/google/accompanist/blob/snapshot/sample/src/main/java/com/google/accompanist/sample/pager/VerticalPagerWithIndicatorSample.kt) show you how to use these. 257 258 259### Integration with Tabs 260 261A common use-case for [`HorizontalPager`][api-horizpager] is to be used in conjunction with a [`TabRow`](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#tabrow) or [`ScrollableTabRow`](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#scrollabletabrow). 262 263<figure> 264 <video width="300" controls loop> 265 <source src="tabs_demo.mp4" type="video/mp4"> 266 Your browser does not support the video tag. 267 </video> 268 <figcaption>HorizontalPager + TabRow</figcaption> 269</figure> 270 271 272Provided in the `pager-indicators` library is a modifier which can be used on a tab indicator like so: 273 274``` kotlin 275val pagerState = rememberPagerState() 276 277TabRow( 278 // Our selected tab is our current page 279 selectedTabIndex = pagerState.currentPage, 280 // Override the indicator, using the provided pagerTabIndicatorOffset modifier 281 indicator = { tabPositions -> 282 TabRowDefaults.Indicator( 283 Modifier.pagerTabIndicatorOffset(pagerState, tabPositions) 284 ) 285 } 286) { 287 // Add tabs for all of our pages 288 pages.forEachIndexed { index, title -> 289 Tab( 290 text = { Text(title) }, 291 selected = pagerState.currentPage == index, 292 onClick = { /* TODO */ }, 293 ) 294 } 295} 296 297HorizontalPager( 298 count = pages.size, 299 state = pagerState, 300) { page -> 301 // TODO: page content 302} 303``` 304 305## Changes in v0.19.0 306 307In 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: 308 309### PagerState 310 311- The `pageCount` parameter on `rememberPagerState()` has been removed, replaced with the `count` parameter on `HorizontalPager()` and `VerticalPager()`. 312- The `animationSpec`, `initialVelocity` and `skipPages` parameters on `animateScrollToPage()` have been removed. The lazy components handle this automatically. 313 314### HorizontalPager & VerticalPager 315 316- Ability to set `contentPadding` (see [above](#content-padding)). 317- Ability to specify a `key` for each page. 318- The `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](#content-padding)). 319- The `infiniteLooping` parameter and feature have been removed. A sample demonstrating how to achieve this effect can be found [here][looping-sample]. 320- The `offscreenLimit` parameter has been removed. We no longer have control of what items are laid out 'off screen'. 321- The `dragEnabled` parameter has removed. 322- `PagerScope` (the page item scope) no longer implements `BoxScope`. 323 324--- 325 326## Usage 327 328``` groovy 329repositories { 330 mavenCentral() 331} 332 333dependencies { 334 implementation "com.google.accompanist:accompanist-pager:<version>" 335 336 // If using indicators, also depend on 337 implementation "com.google.accompanist:accompanist-pager-indicators:<version>" 338} 339``` 340 341### Library Snapshots 342 343Snapshots of the current development version of this library are available, which track the latest commit. See [here](../using-snapshot-version) for more information on how to use them. 344 345--- 346 347## Contributions 348 349Please contribute! We will gladly review any pull requests. 350Make sure to read the [Contributing](../contributing) page first though. 351 352## License 353 354``` 355Copyright 2021 The Android Open Source Project 356 357Licensed under the Apache License, Version 2.0 (the "License"); 358you may not use this file except in compliance with the License. 359You may obtain a copy of the License at 360 361 https://www.apache.org/licenses/LICENSE-2.0 362 363Unless required by applicable law or agreed to in writing, software 364distributed under the License is distributed on an "AS IS" BASIS, 365WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 366See the License for the specific language governing permissions and 367limitations under the License. 368``` 369 370 [api-vertpager]: ../api/pager/pager/com.google.accompanist.pager/-vertical-pager.html 371 [api-horizpager]: ../api/pager/pager/com.google.accompanist.pager/-horizontal-pager.html 372 [currentpage-api]: ../api/pager/pager/com.google.accompanist.pager/-pager-state/current-page.html 373 [currentpageoffset-api]: ../api/pager/pager/com.google.accompanist.pager/-pager-state/current-page-offset.html 374 [calcoffsetpage]: ../api/pager/pager/com.google.accompanist.pager/calculate-current-offset-for-page.html 375 [pagerstate-api]: ../api/pager/pager/com.google.accompanist.pager/remember-pager-state.html 376 [looping-sample]: https://github.com/google/accompanist/blob/main/sample/src/main/java/com/google/accompanist/sample/pager/HorizontalPagerLoopingSample.kt 377