Files
coco 723ce1af5c a
2026-07-03 15:12:48 +08:00

139 lines
4.3 KiB
Markdown

# Tab navigation
!!! success
To use the `TabNavigator` you should first import `cafe.adriel.voyager:voyager-tab-navigator` (see [Setup](../setup.md)).
Voyager provides a specialized navigator for tabs : the `TabNavigator`.
The `Tab` interface, like the `Screen`, has a `Content()` composable function, but also requires a `TabOptions`. 
```kotlin
object HomeTab : Tab {
override val options: TabOptions
@Composable
get() {
val title = stringResource(R.string.home_tab)
val icon = rememberVectorPainter(Icons.Default.Home)
return remember {
TabOptions(
index = 0u,
title = title,
icon = icon
)
}
}
@Composable
override fun Content() {
// ...
}
}
```
!!! info
Since tabs aren't usually reused, its OK to create them as `object`.
The `TabNavigator` unlike the `Navigator`:
* Don't handle [back presses](../back-press.md), because the tabs are siblings
* Don't implements the [Stack API](../stack-api.md), just provides a `current` property
You can use it with a [Scaffold](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#Scaffold\(androidx.compose.ui.Modifier,androidx.compose.material.ScaffoldState,kotlin.Function0,kotlin.Function0,kotlin.Function1,kotlin.Function0,androidx.compose.material.FabPosition,kotlin.Boolean,kotlin.Function1,kotlin.Boolean,androidx.compose.ui.graphics.Shape,androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,kotlin.Function1\)) to easily create the UI for your tabs.
```kotlin
setContent {
TabNavigator(HomeTab) {
Scaffold(
content = {
CurrentTab()
},
bottomBar = {
BottomNavigation {
TabNavigationItem(HomeTab)
TabNavigationItem(FavoritesTab)
TabNavigationItem(ProfileTab)
}
}
)
}
}
```
!!! warning
Like the`CurrentScreen()`, you should use `CurrentTab` instead of `tabNavigator.current.Content()`, because it will save the Tab's subtree for you (see [SaveableStateHolder](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/SaveableStateHolder)).
Use the `LocalTabNavigator` to get the current `TabNavigator`, and `current` to get and set the current tab.
```kotlin
@Composable
private fun RowScope.TabNavigationItem(tab: Tab) {
val tabNavigator = LocalTabNavigator.current
BottomNavigationItem(
selected = tabNavigator.current == tab,
onClick = { tabNavigator.current = tab },
icon = { Icon(painter = tab.icon, contentDescription = tab.title) }
)
}
```
### Sample
![](../media/assets/tab-nav.gif)
!!! info
Source code [here](https://github.com/adrielcafe/voyager/tree/main/samples/android/src/main/java/cafe/adriel/voyager/sample/tabNavigation).
### TabNavigator + Nested Navigator
For more complex use cases, when each tab should have its own independent navigation, like the [Youtube app](https://play.google.com/store/apps/details?id=com.google.android.youtube), you can combine the `TabNavigator` with multiple `Navigator`s.
Let's go back to the [Tab navigation example](tab-navigation.md).
```kotlin
setContent {
TabNavigator(HomeTab) {
// ...
}
}
```
But now, the `HomeTab` will have it's own `Navigator`.
```kotlin
object HomeTab : Screen {
@Composable
override fun Content() {
Navigator(PostListScreen())
}
}
```
That way, we can use the `LocalNavigator` to navigate deeper into `HomeTab`, or the `LocalTabNavigator` to switch between tabs.
```kotlin
class PostListScreen : Screen {
@Composable
private fun GoToPostDetailsScreenButton(post: Post) {
val navigator = LocalNavigator.currentOrThrow
Button(
onClick = { navigator.push(PostDetailsScreen(post.id)) }
)
}
@Composable
private fun GoToProfileTabButton() {
val tabNavigator = LocalTabNavigator.current
Button(
onClick = { tabNavigator.current = ProfileTab }
)
}
}
```