Docs / Bottom bars / 21

BuoyBottomBar

Floating pill whose selected icon lifts over its label, with a buoy in the bottom edge that dives and resurfaces under the next tab.

  • Floating
  • Any tab
  • Icon only

Preview

Light
Buoy bottom bar in light theme, switching between tabs
Dark
Buoy bottom bar in dark theme, switching between tabs

A floating pill of icon-only tabs. The selected icon fills in and lifts over its label, with a small buoy floating half-sunk in the bottom edge underneath. On a new selection the buoy dives below the edge, travels across and bobs back up under the new tab.

Usage

BuoyBarItem has no center item: any tab can be selected, and every slot gets the same width. The bar only shows icons until a tab is selected; the selected one switches to its selectedIcon, lifts it and shows its label underneath. The rest of the time label is read out by TalkBack.

Imports
import com.nameisjayant.bottombar.buoy.BuoyBarItem
import com.nameisjayant.bottombar.buoy.BuoyBottomBar
import com.nameisjayant.bottombar.buoy.BuoyBottomBarDefaults
BuoyBottomBar.kt
val items = listOf(
    BuoyBarItem("Home", Icons.Outlined.Home, Icons.Filled.Home),
    BuoyBarItem("Wallet", Icons.Outlined.AccountBalanceWallet, Icons.Filled.AccountBalanceWallet),
    BuoyBarItem("Analysis", Icons.Outlined.PieChart, Icons.Filled.PieChart),
    BuoyBarItem("Profile", Icons.Outlined.Person, Icons.Filled.Person),
)

BuoyBottomBar(
    items = items,
    selectedIndex = selectedIndex,
    onItemClick = { selectedIndex = it },
    modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
)

Sizing and behavior

The bar is a BarHeight (64dp) tall pill and has no outer margin of its own, so pass one through modifier. The BuoySize (10dp) buoy rests centered on the bottom edge, so only its top half shows. When the selection changes it dives up to DiveDepth (8dp) below its resting spot, deep enough to disappear, and resurfaces under the new tab. The outline is convex, so shadowElevation works on every API level.

Colors

Colors come from BuoyBottomBarDefaults.colors(darkTheme), lightColors(...) and darkColors(...). Start from either palette and override only what you need.

ColorUsed for
containerColorThe bar
selectedContentColorIcon and label of the selected tab
unselectedContentColorIcons of the other tabs
buoyColorThe buoy (defaults to selectedContentColor)
Theming
BuoyBottomBar(
    items = items,
    selectedIndex = selectedIndex,
    onItemClick = { selectedIndex = it },
    // Follow your app's theme instead of the system one.
    colors = BuoyBottomBarDefaults.colors(darkTheme = isDarkTheme),
)

API

The full signature of the composable and its item class:

com.nameisjayant.bottombar.buoy
fun BuoyBottomBar(
    items: List<BuoyBarItem>,
    selectedIndex: Int,
    onItemClick: (index: Int) -> Unit,
    modifier: Modifier = Modifier,
    colors: BuoyBottomBarColors = BuoyBottomBarDefaults.colors(),
    labelStyle: TextStyle = BuoyBottomBarDefaults.LabelStyle,
    shadowElevation: Dp = 0.dp,
    windowInsets: WindowInsets = BuoyBottomBarDefaults.windowInsets,
)

class BuoyBarItem(
    val label: String,
    val icon: ImageVector,
    val selectedIcon: ImageVector = icon,
)