Back to Material Components Android

Bottom navigation (Navigation bar)

docs/components/BottomNavigation.md

1.13.023.3 KB
Original Source
<!--docs: title: "Bottom navigation" layout: detail section: components excerpt: "Bottom navigation bars make it easy to explore and switch between top-level views in a single tap." iconId: bottom_navigation path: /catalog/bottom-navigation/ -->

Bottom navigation (Navigation bar)

Navigation bar lets people switch between UI views on smaller devices.

Note: The design name of this component has been changed from Bottom navigation to Navigation bar. However, Material's Android implementation remains as BottomNavigationView.

Navigation bar on compact and medium window sizes

Navigation bars can have three to five destinations. The nav bar is positioned at the bottom of screens for convenient access. Each destination is represented by an icon and label text.

Note: Images use various dynamic color schemes.

Design & API documentation

Anatomy

  1. Container
  2. Icon
  3. Label text
  4. Active indicator
  5. Small badge (optional)
  6. Large badge (optional)
  7. Large badge label

More details on anatomy items in the component guidelines.

M3 Expressive

M3 Expressive update

Before you can use Material3Expressive component styles, follow the Material3Expressive themes setup instructions.

A new expressive style for the bottom navigation bar has been introduced that's shorter and supports horizontal navigation items in medium-sized windows. More on M3 Expressive

Color:

  • Active label changed from on-surface-variant to secondary

M3 Expressive styles

Changes from M3:

  • Height: From 80dp to 64dp
  • Color: New expressive colors!
  • Top item padding: From 12dp to 6dp
  • Bottom item padding: From 16dp to 6dp
  • Label text is no longer bolded when selected
  • Active indicator: From 64dp to 56dp
  • New horizontal item configuration on medium and larger window sizes (greater than or equal to 600dp):
    • Icon moves from top to start of item
    • Instead of being a set width based on the item count, item width is based on content with a max width
    • Item gravity: From top center to center

The default style for bottom navigation bar is:

xml
<item name="bottomNavigationStyle">@style/Widget.Material3Expressive.BottomNavigationView</item>

Key properties

Container attributes

ElementAttributeRelated methodsDefault value
Colorapp:backgroundTintN/A?attr/colorSurfaceContainer
Elevationapp:elevationsetElevation3dp
Compat Shadow (deprecated)compatShadowEnabledN/Afalse

Note: compatShadowEnabled has no effect, as the library no longer supports pre-Lollipop.

ElementAttributeRelated methodsDefault value
Menu resourceapp:menuinflateMenu
getMenuN/A
Ripple (inactive)app:itemRippleColorsetItemRippleColor
getItemRippleColorVariations of ?attr/colorPrimary and ?attr/colorOnSurfaceVariant (see all states)
Ripple (active)""Variations of ?attr/colorPrimary (see all states)
Label visibility modeapp:labelVisibilityModesetLabelVisibilityMode
getLabelVisibilityModeLABEL_VISIBILITY_AUTO
Item Gravityapp:itemGravitysetItemGravity
getItemGravityTOP_CENTER

Active indicator attributes

ElementAttributeRelated methodsDefault value
Colorandroid:colorsetItemActiveIndicatorColor
getItemActiveIndicatorColor?attr/colorSecondaryContainer
Widthandroid:widthsetItemActiveIndicatorWidth
getItemActiveIndicatorWidth56dp
Heightandroid:heightsetItemActiveIndicatorHeight
getItemActiveIndicatorHeight32dp
Shapeapp:shapeAppearancesetItemActiveIndicatorShapeAppearance
getItemActiveIndicatorShapeAppearance50% rounded
Margin horizontalapp:marginHorizontalsetItemActiveIndicatorMarginHorizontal
getItemActiveIndicatorMarginHorizontal4dp
Padding between indicator and labelapp:activeIndicatorLabelPaddingsetActiveIndicatorLabelPadding
getActiveIndicatorLabelPadding4dp
Expanded Widthapp:expandedWidthsetItemActiveIndicatorExpandedWidth
getItemActiveIndicatorExpandedWidthHUG
Expanded Heightapp:expandedHeightsetItemActiveIndicatorExpandedHeight
getItemActiveIndicatorExpandedHeight56dp
Expanded Margin horizontalapp:expandedMarginHorizontalsetItemActiveIndicatorExpandedMarginHorizontal
getItemActiveIndicatorExpandedMarginHorizontal20dp
Expanded Start Paddingapp:expandedActiveIndicatorPaddingStartsetItemExpandedActiveIndicatorPadding16dp
Expanded End Paddingapp:expandedActiveIndicatorPaddingEndsetItemExpandedActiveIndicatorPadding16dp
Expanded Top Paddingapp:expandedActiveIndicatorPaddingTopsetItemExpandedActiveIndicatorPadding0dp
Expanded Bottom Paddingapp:expandedActiveIndicatorPaddingBottomsetItemExpandedActiveIndicatorPadding0dp

Note: The expanded active indicator refers to the active indicator that expands to wrap the content of the navigation bar item when the itemIconGravity value is equal to START.

Icon attributes

ElementAttributeRelated methodsDefault value
Iconandroid:icon in the menu resourceN/AN/A
Sizeapp:itemIconSizesetItemIconSize
setItemIconSizeRes
getItemIconSize24dp
Color (inactive)app:itemIconTintsetItemIconTintList
getItemIconTintList?attr/colorOnSurfaceVariant (see all states)
Color (active)""?attr/colorOnSecondaryContainer (see all states)
Gravityapp:itemIconGravitysetItemIconGravity
getItemIconGravityTOP
Icon label horizontal paddingapp:iconLabelHorizontalSpacingsetIconLabelHorizontalSpacing
getIconLabelHorizontalSpacing4dp

Text label attributes

ElementAttributeRelated methodsDefault value
Text labelandroid:title in the menu resourceN/AN/A
Color (inactive)app:itemTextColorsetItemTextColor
getItemTextColor?attr/colorOnSurfaceVariant (see all states)
Color (active)""?attr/colorOnSurface (see all states)
Typography (inactive)app:itemTextAppearanceInactive
app:horizontalItemTextAppearanceInactivesetItemTextAppearanceInactive
getItemTextAppearanceInactive
setHorizontalItemTextAppearanceInactive
getHorizontalItemTextAppearanceInactive?attr/textAppearanceTitleSmall
Typography (active)app:itemTextAppearanceActive
app:horizontalItemTextAppearanceActivesetItemTextAppearanceActive
getItemTextAppearanceActive
setHorizontalItemTextAppearanceActive
getHorizontalItemTextAppearanceActive?attr/textAppearanceTitleSmall
Typography (active)app:itemTextAppearanceActiveBoldEnabledsetItemTextAppearanceActiveBoldEnabledtrue
Max linesapp:labelMaxLinessetLabelMaxLines
getLabelMaxLines1
Scale with font sizeapp:scaleLabelWithFontSizesetScaleLabelTextWithFont
getScaleLabelTextWithFontfalse

Styles

ElementStyleContainer colorIcon/Text label color (inactive)Icon/Text label color (active)Theme attribute
Default styleWidget.Material3.BottomNavigationView?attr/colorSurface?attr/colorOnSurfaceVariantIcon: ?attr/colorOnSecondaryContainer
Text: ?attr/colorOnSurface?attr/bottomNavigationStyle

For the full list, see styles, navigation attributes, and navigation bar attributes.

Code implementation

Before you can use the Material navigation bar, you need to add a dependency to the Material components for Android library. For more information, go to the Getting started page.

Adding navigation bar

A typical layout looks like this:

xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
  ...
  <com.google.android.material.bottomnavigation.BottomNavigationView
      android:id="@+id/bottom_navigation"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:menu="@menu/bottom_navigation_menu" />

</LinearLayout>

The @menu/bottom_navigation_menu resource should point to a file named bottom_navigation_menu.xml inside a menu resource directory:

xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:id="@+id/item_1"
      android:enabled="true"
      android:icon="@drawable/icon_1"
      android:title="@string/text_label_1"/>
  <item
      android:id="@+id/item_2"
      android:enabled="true"
      android:icon="@drawable/icon_2"
      android:title="@string/text_label_2"/>
</menu>

Note: BottomNavigationView does not support more than 5 menu items.

In code:

kt
NavigationBarView.OnItemSelectedListener { item ->
    when(item.itemId) {
        R.id.item_1 -> {
            // Respond to navigation item 1 click
            true
        }
        R.id.item_2 -> {
            // Respond to navigation item 2 click
            true
        }
        else -> false
    }
}

There's also a method for detecting when navigation items have been reselected:

kt
bottomNavigation.setOnItemReselectedListener { item ->
    when(item.itemId) {
        R.id.item_1 -> {
            // Respond to navigation item 1 reselection
        }
        R.id.item_2 -> {
            // Respond to navigation item 2 reselection
        }
    }
}

That results in:

Note: We have deprecated the BottomNavigationView#setOnNavigationItemSelectedListener and BottomNavigationView#setOnNavigationItemReselectedListener methods in favor of the listeners in NavigationBarView. This allows you to share selection handling code between the BottomNavigation and NavigationRail view elements.

The following example shows a navigation bar with four icons:

In layout.xml:

xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

  <com.google.android.material.bottomnavigation.BottomNavigationView
      android:id="@+id/bottom_navigation"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:menu="@menu/bottom_navigation_menu" />

</LinearLayout>

In bottom_navigation_menu.xml inside a menu resource directory:

xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:id="@+id/page_1"
      android:enabled="true"
      android:icon="@drawable/ic_star"
      android:title="@string/page_1"/>
  <item
      android:id="@+id/page_2"
      android:enabled="true"
      android:icon="@drawable/ic_star"
      android:title="@string/page_2"/>
  <item
      android:id="@+id/page_3"
      android:enabled="true"
      android:icon="@drawable/ic_star"
      android:title="@string/page_3"/>
  <item
      android:id="@+id/page_4"
      android:enabled="true"
      android:icon="@drawable/ic_star"
      android:title="@string/page_4"/>
</menu>

In code:

kt
bottomNavigation.selectedItemId = R.id.page_2

Adding navigation bar on larger screens

On medium screen sizes and larger, navigation bars are recommended to be a horizontal item configuration, by setting app:itemIconGravity to be start instead of top. You can do this by setting alternative layouts identified by resource qualifiers.

Here's an example:

xml
<com.google.android.material.bottomnavigation.BottomNavigationView
  android:id="@+id/bottom_navigation_bar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="bottom"
  app:itemIconGravity="start"
  app:itemGravity="center"
  app:menu="@menu/bottom_navigation_menu"/>

Making navigation bar accessible

You should set an android:title for each of your menu items so that screen readers like TalkBack can properly announce what each navigation item represents:

xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      ...
      android:title="@string/text_label"/>
  ...
</menu>

The labelVisibilityMode attribute can be used to adjust the behavior of the text labels for each navigation bar item. There are four visibility modes:

  • LABEL_VISIBILITY_AUTO (default): The label behaves as “labeled” when there are 3 items or less, or “selected” when there are 4 items or more
  • LABEL_VISIBILITY_SELECTED: The label is only shown on the selected navigation item
  • LABEL_VISIBILITY_LABELED: The label is shown on all navigation items
  • LABEL_VISIBILITY_UNLABELED: The label is hidden for all navigation items

Adding badges

Initialize and show a BadgeDrawable associated with menuItemId, subsequent calls to this method will reuse the existing BadgeDrawable:

kt
var badge = bottomNavigation.getOrCreateBadge(menuItemId)
badge.isVisible = true
// An icon only badge will be displayed unless a number or text is set:
badge.number = 99  // or badge.text = "New"

As a best practice, if you need to temporarily hide the badge, for instance until the next notification is received, change the visibility of BadgeDrawable:

kt
val badgeDrawable = bottomNavigation.getBadge(menuItemId)
    if (badgeDrawable != null) {
        badgeDrawable.isVisible = false
        badgeDrawable.clearNumber()  // or badgeDrawable.clearText()
    }

To remove any BadgeDrawables that are no longer needed:

kt
bottomNavigation.removeBadge(menuItemId)

See the Badges documentation for more information about badges.

Customizing navigation bar

Theming a navigation bar

Navigation bars support the customization of color and typography.

Navigation bar theming example

API and source code:

The following example shows a navigation bar with Material theming.

Implementing navigation bar theming

Use theme attributes and a style in res/values/styles.xml, which applies to all navigation bars and affects other components:

xml
<style name="Theme.App" parent="Theme.Material3.*">
    ...
    <item name="colorSurface">@color/shrine_theme_light_surface</item>
    <item name="colorOnSurfaceVariant">@color/shrine_theme_light_onSurfaceVariant</item>
</style>

Use a default style theme attribute, styles, and a theme overlay, which apply to all navigation bars but do not affect other components:

xml
<style name="Theme.App" parent="Theme.Material3.*">
    ...
    <item name="bottomNavigationStyle">@style/Widget.App.BottomNavigationView</item>
</style>

<style name="Widget.App.BottomNavigationView" parent="Widget.Material3.BottomNavigationView">
    <item name="materialThemeOverlay">@style/ThemeOverlay.App.BottomNavigationView</item>
</style>

<style name="ThemeOverlay.App.BottomNavigationView" parent="">
    <item name="colorSurface">@color/shrine_theme_light_surface</item>
    <item name="colorOnSurfaceVariant">@color/shrine_theme_light_onSurfaceVariant</item>
</style>

Use the style in the layout, which affects only this specific navigation bar:

xml
<com.google.android.material.bottomnavigation.BottomNavigationView
    ...
    style="@style/Widget.App.BottomNavigationView"
/>