Jetpack Compose: Tabs with swiping
Tags: android, jetpackcompose, kotlin
Published: 2021-11-13 11:24:32

---
title: Jetpack Compose: Tabs with swiping
published: true
date: 2021-11-13 11:12:49 UTC
tags: Android,JetpackCompose,Kotlin
canonical_url: https://www.rockandnull.com/jetpack-compose-swipe-pager/
---

 ![Jetpack Compose: Tabs with swiping](https://images.unsplash.com/photo-1533747122906-9ac6b6709832?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHRhYnN8ZW58MHx8fHwxNjM2ODAwNDY1&ixlib=rb-1.2.1&q=80&w=2000)

Creating an interface with tabs is quite common in the Android app world.

In the old world, creating a tabbed interface would require adapters and sometimes fragments holding the tab data.

But using Jetpack Compose these complexities belong to the past. Creating a UI with tabs is quick and easy.

Of course, there are things that we were taking for granted in the past that are not (yet) supported natively. The ability to swipe to change tabs is one of those things. But fear not. There are ways to get this kind of functionality using a library.

## Building the tabs UI

Creating a tabs strip and switching content when a tab button is selected is quite easy. No need for external dependencies, everything is built-in into Compose.

![Jetpack Compose: Tabs with swiping](https://www.rockandnull.com/content/images/2021/11/Screenshot-2021-11-13-at-10.43.21.png)

```
@Composable
fun tabs() {
    var tabIndex by remember { mutableStateOf(0) } // 1.
    val tabTitles = listOf("Hello", "There", "World")
    Column { // 2.
        TabRow(selectedTabIndex = tabIndex) { // 3.
            tabTitles.forEachIndexed { index, title ->
                Tab(selected = tabIndex == index, // 4.
                    onClick = { tabIndex = index },
                    text = { Text(text = title) }) // 5.
            }
        }
        when (tabIndex) { // 6.
            0 -> Text("Hello content")
            1 -> Text("There content")
            2 -> Text("World content")
        }
    }
}
```

1. When recomposition takes place, we need to remember which tab was selected. Otherwise, every time the selected index will switch to the first position.
2. We use a `Column` to vertically stack the tab strip row and the tab contents.
3. This `TabRow` composable holds the tab strip containing the tab buttons.
4. Each tab button is represented by a `Tab` composable.
5. Customize the text on the tab button (e.g. font, color, etc) by customizing the `Text` composable here.
6. Switch contents depending on which tab is selected.

## Tabs with swiping

When I see an interface with tabs, my first reaction is to swipe left/right to explore the rest of the tabs. I almost forgot that you can actually click on the tab titles to switch tabs.

This behavior is not included in the previous example of building a basic UI with tabs. And it's not included in Compose in general.

Thankfully, there's the [Accompanist](https://google.github.io/accompanist/) library that provides various essential but missing functionality from Compose. The [Pager](https://google.github.io/accompanist/pager/) library is what is needed in this case. It provides the ability to swipe on the current tab contents and change tabs.

To [install this library](https://google.github.io/accompanist/pager/#usage), add the following to your `build.gradle` ([check for the latest version](https://search.maven.org/search?q=g:com.google.accompanist)):

```
implementation "com.google.accompanist:accompanist-pager:0.21.2-beta"
implementation "com.google.accompanist:accompanist-pager-indicators:0.21.2-beta"

@ExperimentalPagerApi // 1.
@Preview
@Composable
fun tabsWithSwiping() {
    var tabIndex by remember { mutableStateOf(0) }
    val tabTitles = listOf("Hello", "There", "World")
    val pagerState = rememberPagerState() // 2.
    Column {
        TabRow(selectedTabIndex = tabIndex,
            indicator = { tabPositions -> // 3.
                TabRowDefaults.Indicator(
                    Modifier.pagerTabIndicatorOffset(
                        pagerState,
                        tabPositions
                    )
                )
            }) {
            tabTitles.forEachIndexed { index, title ->
                Tab(selected = tabIndex == index,
                    onClick = { tabIndex = index },
                    text = { Text(text = title) })
            }
        }
        HorizontalPager( // 4.
            count = tabTitles.size,
            state = pagerState,
        ) { tabIndex ->
            Text(
                tabIndex.toString(),
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.White)
            )
        }
    }
}
```

1. This is an experimental API that might change in the future. To acknowledge this, you need to annotate each composable method that uses the Pager library.
2. Similar to the tab index, the pager state needs to be retained when recomposition takes place.
3. This is necessary to synchronize the pager with the tab row indicator. Without this, the indicator showing which tab is selected will go out of sync when swiping through the tabs.
4. This is the pager that allows the swiping of the content. Given the tab index, you can populate the appropriate content for each tab.

You can now have tabs and the ability to swipe between them.

Happy coding!