« Horizontal and Vertical ViewPager in Jetpack Compose

In Jetpack Compose we don’t have anything by default like ViewPager that we could use, but we have a solution for that. We are going to use Accompanist Pager library. First, we are gonna add the dependency to the app-level build.gradle file:

1implementation "com.google.accompanist:accompanist-pager:0.28.0"

There are two pagers avaiable

  1. HorizontalPager
  2. VerticalPager

HorizontalPager

HorizontalPager is composable where content is ordered horizontally and displayed as pages. We are going to create a data class called HorizontalPagerContent which will represent horizontal pager content.

1data class HorizontalPagerContent(
2 val title: String,
3 val subtitle: String,
4 val description: String
5)

Next is to create a list of items that will be shown in this pager:

1fun createItems() = listOf(
2 HorizontalPagerContent(title = "Title1", subtitle = "Subtitle1", description = "Description1"),
3 HorizontalPagerContent(title = "Title2", subtitle = "Subtitle2", description = "Description2"),
4 HorizontalPagerContent(title = "Title3", subtitle = "Subtitle3", description = "Description3"),
5 HorizontalPagerContent(title = "Title4", subtitle = "Subtitle4", description = "Description4"),
6 HorizontalPagerContent(title = "Title5", subtitle = "Subtitle5", description = "Description5")
7)

HorizontalPager has two mandatory parameters and they are count: Int and content: @Composable PagerScope.(page: Int) -> Unit. One optional parameter is state`: PagerState which represents the state of the pager. That state you can use to get the current page, page count, and to scroll to the specific page.

1@OptIn(ExperimentalPagerApi::class)
2@Composable
3fun HorizontalPagerScreen() {
4 Column(
5 modifier = Modifier
6 .fillMaxSize()
7 .padding(30.dp)
8 ) {
9 val items = createItems()
10 val pagerState = rememberPagerState()
11
12 HorizontalPager(
13 count = items.size,
14 state = pagerState,
15 modifier = Modifier.weight(1f)
16 ) { currentPage ->
17 Column(
18 modifier = Modifier.fillMaxSize()
19 ) {
20 Text(
21 text = items[currentPage].title,
22 style = MaterialTheme.typography.h2
23 )
24 Spacer(modifier = Modifier.height(10.dp))
25 Text(
26 text = items[currentPage].subtitle,
27 style = MaterialTheme.typography.h4
28 )
29 Spacer(modifier = Modifier.height(10.dp))
30 Text(
31 text = items[currentPage].description,
32 style = MaterialTheme.typography.body1
33 )
34 }
35 }
36
37 val coroutineScope = rememberCoroutineScope()
38 Button(
39 onClick = {
40 coroutineScope.launch {
41 pagerState.animateScrollToPage(page = 2)
42 }
43 },
44 modifier = Modifier.align(Alignment.CenterHorizontally)
45 ) {
46 Text(text = "Scroll to the third page")
47 }
48 }
49}

At the end of our main composable, we can see a button that will, when pressed, scroll to the third page.

The next thing that we are gonna add is indicators and tabs. For that we need to add a new accompanist library:

1implementation "com.google.accompanist:accompanist-pager-indicators:0.28.0"

Adding indicators is very simple, we just need to add HorizontalPagerIndicator and pass pagerState.

1HorizontalPagerIndicator(
2 pagerState = pagerState,
3 modifier = Modifier
4 .align(Alignment.CenterHorizontally)
5 .padding(16.dp),
6)

Now let’s add tabs, we are going to create a new composable called HorizontalTabs, and in that composable, we will use TabRow and Tab.

1@OptIn(ExperimentalPagerApi::class)
2@Composable
3fun HorizontalTabs(
4 items: List<HorizontalPagerContent>,
5 pagerState: PagerState,
6 scope: CoroutineScope
7) {
8 TabRow(
9 selectedTabIndex = pagerState.currentPage,
10 indicator = { tabPositions ->
11 TabRowDefaults.Indicator(
12 modifier = Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
13 )
14 }
15 ) {
16 items.forEachIndexed { index, item ->
17 Tab(
18 selected = pagerState.currentPage == index,
19 onClick = {
20 scope.launch {
21 pagerState.animateScrollToPage(page = index)
22 }
23 }
24 ) {
25 Text(text = item.title)
26 }
27 }
28 }
29}

VerticalPager

VerticalPager is composable where content is ordered vertically and displayed as pages.

Implementing VerticalPager is basically the same thing as implementing HorizontalPager.

You just need to use VerticalPager instead of HorizontalPager and for indicators you use VerticalPagerIndicator instead of HorizontalPagerIndicator.

1@OptIn(ExperimentalPagerApi::class)
2@Composable
3fun VerticalPagerScreen() {
4 Column(
5 modifier = Modifier
6 .fillMaxSize()
7 .padding(30.dp)
8 ) {
9 val items = createItems()
10 val pagerState = rememberPagerState()
11 val coroutineScope = rememberCoroutineScope()
12
13 Row(
14 modifier = Modifier.weight(1f)
15 ) {
16 VerticalPager(
17 count = items.size,
18 state = pagerState,
19 modifier = Modifier.weight(1f)
20 ) { currentPage ->
21 Column(
22 modifier = Modifier.fillMaxSize()
23 ) {
24 Text(
25 text = items[currentPage].title,
26 style = MaterialTheme.typography.h2
27 )
28 Spacer(modifier = Modifier.height(10.dp))
29 Text(
30 text = items[currentPage].subtitle,
31 style = MaterialTheme.typography.h4
32 )
33 Spacer(modifier = Modifier.height(10.dp))
34 Text(
35 text = items[currentPage].description,
36 style = MaterialTheme.typography.body1
37 )
38 }
39 }
40
41 VerticalPagerIndicator(
42 pagerState = pagerState,
43 modifier = Modifier
44 .align(Alignment.CenterVertically)
45 .padding(16.dp),
46 )
47 }
48
49 Button(
50 onClick = {
51 coroutineScope.launch {
52 pagerState.animateScrollToPage(page = 2)
53 }
54 },
55 modifier = Modifier.align(Alignment.CenterHorizontally)
56 ) {
57 Text(text = "Scroll to the third page")
58 }
59 }
60}

For full class code to try out the code yourself:

https://github.com/anishakd4/JetpackComposeBottomSheet