Tab navigation¶
Success
To use the TabNavigator
you should first import cafe.adriel.voyager:voyager-tab-navigator
(see Setup).
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
.
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, because the tabs are siblings
- Don’t implements the Stack API, just provides a
current
property
You can use it with a Scaffold to easily create the UI for your tabs.
setContent {
TabNavigator(HomeTab) {
Scaffold(
content = {
CurrentTab()
},
bottomBar = {
BottomNavigation {
TabNavigationItem(HomeTab)
TabNavigationItem(FavoritesTab)
TabNavigationItem(ProfileTab)
}
}
)
}
}
Warning
Like theCurrentScreen()
, you should use CurrentTab
instead of tabNavigator.current.Content()
, because it will save the Tab’s subtree for you (see SaveableStateHolder).
Use the LocalTabNavigator
to get the current TabNavigator
, and current
to get and set the current tab.
@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¶
Info
Source code here.
TabNavigator + Nested Navigator¶
For more complex use cases, when each tab should have its own independent navigation, like the Youtube app, you can combine the TabNavigator
with multiple Navigator
s.
Let’s go back to the Tab navigation example.
setContent {
TabNavigator(HomeTab) {
// ...
}
}
But now, the HomeTab
will have it’s own Navigator
.
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.
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 }
)
}
}