material-components-android

Navigation Rail

Navigation rail provides access to primary destinations in your app on tablet and desktop screens.

The navigation rail container is 80 dp wide by default.

Contents

Design and API Documentation

Using navigation rail

Before you can use the Material Navigation Rail, you need to add a dependency to the Material Components for Android library. For more information, go to the Getting started page.

A typical layout will look similar to this:

<com.google.android.material.navigationrail.NavigationRailView
    android:id="@+id/navigation_rail"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    app:menu="@menu/navigation_rail_menu" />

Note: The width of a NavigationRailView will be 80dp wide by default.The width of the rail can be changed by setting the android:layout_width attribute to a specific DP value.

In navigation_rail_menu.xml inside a menu resource directory:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:id="@+id/alarms"
      android:enabled="true"
      android:icon="@drawable/icon_alarms"
      android:title="@string/alarms_destination_label"/>
  <item
      android:id="@+id/schedule"
      android:enabled="true"
      android:icon="@drawable/icon_clock"
      android:title="@string/schedule_destination_label"/>
  <item
      android:id="@+id/timer"
      android:enabled="true"
      android:icon="@drawable/icon_sand_clock"
      android:title="@string/timer_destination_label"/>
  <item
      android:id="@+id/stopwatch"
      android:enabled="true"
      android:icon="@drawable/icon_stop_watch"
      android:title="@string/stopwatch_destination_label"/>
</menu>

NavigationRailView displays three to no more than seven app destinations when collapsed, and can include a header view. Each destination is represented by an icon and a text label.

You can also define submenus for the Navigation Rail like below:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/submenu_1"
        android:title="@string/subheader_1_name">
        <menu>
            <item
                android:id="@+id/timer"
                android:enabled="true"
                android:icon="@drawable/icon_sand_clock"
                android:title="@string/timer_destination_label"/>
            <item
                android:id="@+id/stopwatch"
                android:enabled="true"
                android:icon="@drawable/icon_stop_watch"
                android:title="@string/stopwatch_destination_label"/>
        </menu>
    </item>
    <item
        android:id="@+id/alarms"
        android:enabled="true"
        android:icon="@drawable/icon_alarms"
        android:title="@string/alarms_destination_label"/>
    <item
        android:id="@+id/schedule"
        android:enabled="true"
        android:icon="@drawable/icon_clock"
        android:title="@string/schedule_destination_label"/>
</menu>

Navigation rails are collapsed by default. When collapsed, only menu items that are not under a submenu will be shown, up to a limit of 7. There is no limit of items shown when expanded.

You will need to set listeners for the menu items in code:

// Listeners are defined on the super class NavigationBarView
// to support both NavigationRail and BottomNavigation with the
// same listeners
NavigationBarView.OnNavigationItemSelectedListener { item ->
    when(item.itemId) {
        R.id.alarms -> {
            // Respond to alarm navigation item click
            true
        }
        R.id.schedule -> {
            // Respond to schedule navigation item click
            true
        }
        else -> false
    }
}

There’s also a method for detecting if navigation items have been reselected:

navigationRail.setOnNavigationItemReselectedListener { item ->
    when(item.itemId) {
        R.id.item1 -> {
            // Respond to navigation item 1 reselection
        }
        R.id.item2 -> {
            // Respond to navigation item 2 reselection
        }
    }
}

Which results in:

The navigation rail container is 72 dp wide by default.

By default, Navigation rail adds top and bottom padding according to top and bottom window insets—helping the header layout and menu items dodge system spaces. This is controlled by the android:fitsSystemWindowInsets attribute, which is set to true by default. To remove this behavior, set android:fitsSystemWindowInsets to false or opt in or out of the top and bottom insets independently by using app:paddingTopSystemWindowInsets and app:paddingBottomSystemWindowInsets.

API and source code:

Making navigation rail 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:

<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 item. There are four visibility modes:

Adding a header view

The rail provides a convenient container for anchoring a header view, such as a FloatingActionButton or a logo, to the top of the rail, using the app:headerLayout attribute.

Navigation rail with badges

<com.google.android.material.navigationrail.NavigationRailView
    android:id="@+id/navigation_rail"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:headerLayout="@layout/navigation_rail_fab"
    app:menu="@menu/navigation_rail_menu" />

The header view can also be added or removed at runtime using the following methods:

Method Description
void addHeaderView(@NonNull View view) The specified header view will be attached to the NavigationRailView, so that it will appear at the top. If the view already has a header view attached to it, it will be removed first.
void removeHeaderView() Detaches the current header view if any, from the Navigation Rail.

The following methods can be used to manipulate the header view at runtime.

Method Description
@Nullable view getHeaderView() Returns an instance of the header view associated with the Navigation Rail, null if none was currently attached.

Expanding the Navigation Rail

You can call navigationRailView.expand() and navigationRailView.collapse() to expand and collapse the navigation rail.

When collapsed, only menu items not under a submenu will be shown, up to a limit of 7.

When expanded, all menu items will be shown, including submenu items.

Navigation rails are collapsed by default which animates into the expanded navigation rail when expanded. If the content beside the navigation rail takes into account the size of the navigation rail (ie., through setting constraints in ConstraintLayout or layout weights) then the content will also be animated to shrink. This animation is taken care of by a ChangeBounds Transition; any animations during the expansion of the navigation rail should be ‘turned off’ as it could result in a strange animation due to the Transition.

Collapsed Navigation Rail Expanded Navigation Rail
Collapsed navigation rail Expanded navigation rail

Adding badges

Rail icons can include badges on the upper right corner of the icon. Badges convey dynamic information about the associated destination, such as counts or status.

Navigation rail with badges

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

var badge = navigationRail.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 best practice, if you need to temporarily hide the badge, for example until the next notification is received, change the visibility of BadgeDrawable:

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

To remove any BadgeDrawables that are no longer needed:

navigationRail.removeBadge(menuItemId)

See the BadgeDrawable documentation for more information.

The following example shows a navigation rail with four icons:

In navigation_rail_menu.xml inside a menu resource directory:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:id="@+id/alarms"
      android:enabled="true"
      android:icon="@drawable/icon_alarm"
      android:title="@string/alarms_destination_label"/>
  <item
      android:id="@+id/schedule"
      android:enabled="true"
      android:icon="@drawable/icon_clock"
      android:title="@string/schedule_destination_label"/>
  <item
      android:id="@+id/timers"
      android:enabled="true"
      android:icon="@drawable/icon_sand_clock"
      android:title="@string/timers_destination_label"/>
  <item
      android:id="@+id/stopwatch"
      android:enabled="true"
      android:icon="@drawable/icon_stop_watch"
      android:title="@string/stopwatch_destination_label"/>
</menu>

In code:

navigationRail.selectedItemId = R.id.images

Anatomy and key properties

The following is an anatomy diagram for the navigation rail:

Navigation rail anatomy diagram

  1. Container
  2. Header - menu icon (optional)
  3. Header - Floating action button (optional)
  4. Icon - active
  5. Active indicator
  6. Label text - active (optional)
  7. Icon - inactive
  8. Label text - inactive (optional)
  9. Large badge (optional)
  10. Large badge label (optional)
  11. Badge (optional)

Container attributes

Element Attribute Related methods Default value
Color app:backgroundTint N/A ?attr/colorSurface
Elevation app:elevation setElevation 0dp
Fits system windows android:fitsSystemWindows getFitsSystemWindows
setFitsSystemWindows
true
Padding top system window insets app:paddingTopSystemWindowInsets N/A null
Padding bottom system window insets app:paddingBottomSystemWindowInsets N/A null
Top margin app:contentMarginTop N/A N/A
Scrolling app:scrollingEnabled N/A false

Header attributes

Element Attribute Related methods Default value
Header view app:headerLayout addHeaderView
removeHeaderView
getHeaderView
N/A
Header bottom margin app:headerMarginBottom N/A 8dp

See the FAB documentation for more attributes.

Element Attribute Related methods Default value
Menu gravity app:menuGravity setMenuGravity
getMenuGravity
TOP\|CENTER_HORIZONTAL
Dividers app:submenuDividersEnabled setSubmenuDividersEnabled
getSubmenuDividersEnabled
false

Note: If dividers are enabled, they will be between all submenus, which are only visible when expanded.

Element Attribute Related methods Default value
Menu resource app:menu inflateMenu
getMenu
N/A
Ripple (inactive) app:itemRippleColor setItemRippleColor
getItemRippleColor
?attr/colorPrimary at 12% (see all states)
Ripple (active) app:itemRippleColor setItemRippleColor
getItemRippleColor
?attr/colorPrimary at 12% (see all states)
Label visibility mode app:labelVisibilityMode setLabelVisibilityMode
getLabelVisibilityMode
LABEL_VISIBILITY_AUTO
Item minimum height app:itemMinHeight setItemMinimumHeight
getItemMinimumHeight
NO_ITEM_MINIMUM_HEIGHT
Collapsed item minimum height app:collapsedItemMinHeight setCollapsedItemMinimumHeight
getCollapsedItemMinimumHeight
NO_ITEM_MINIMUM_HEIGHT
Expanded item minimum height app:expandedItemMinHeight setExpandedItemMinimumHeight
getExpandedItemMinimumHeight
NO_ITEM_MINIMUM_HEIGHT
Item spacing app:itemSpacing setItemSpacing
getItemSpacing
0dp
Item Gravity app:itemGravity setItemGravity
getItemGravity
TOP_CENTER

Note: If there’s not enough room, itemMinHeight and itemSpacing may not be respected in order to fit the items.

Active indicator attributes

Element Attribute Related methods Default value
Color android:color setItemActiveIndicatorColor
getItemActiveIndicatorColor
?attr/colorSecondaryContainer
Width android:width setItemActiveIndicatorWidth
getItemActiveIndicatorWidth
56dp
Height android:height setItemActiveIndicatorHeight
getItemActiveIndicatorHeight
32dp
Shape app:shapeAppearance setItemActiveIndicatorShapeAppearance
getItemActiveIndicatorShapeAppearance
50% rounded
Margin horizontal app:marginHorizontal setItemActiveIndicatorMarginHorizontal
getItemActiveIndicatorMarginHorizontal
4dp
Padding between indicator and label app:activeIndicatorLabelPadding setActiveIndicatorLabelPadding
getActiveIndicatorLabelPadding
4dp
Expanded Width expandedWidth setItemExpandedActiveIndicatorWidth
getItemExpandedActiveIndicatorWidth
HUG
Expanded Height expandedHeight setItemExpandedActiveIndicatorHeight
getItemExpandedActiveIndicatorHeight
56dp
Expanded Margin horizontal app:expandedMarginHorizontal setItemExpandedActiveIndicatorMarginHorizontal
getItemExpandedActiveIndicatorMarginHorizontal
20dp

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

Icon attributes

Element Attribute Related methods Default value
Icon android:icon in the menu resource N/A N/A
Size app:itemIconSize setItemIconSize
setItemIconSizeRes
getItemIconSize
24dp
Color (inactive) app:itemIconTint setItemIconTintList
getItemIconTintList
?attr/colorOnSurfaceVariant
Color (active) app:itemIconTint setItemIconTintList
getItemIconTintList
?attr/colorOnSecondaryContainer
Gravity app:itemIconGravity setItemIconGravity
getItemIconGravity
TOP
Icon label horizontal padding app:iconLabelHorizontalSpacing setIconLabelHorizontalSpacing
getIconLabelHorizontalSpacing
8dp

Text label attributes

Element Attribute Related methods Default value
Text label android:title in the menu resource N/A N/A
Color (inactive) app:itemTextColor setItemTextColor
getItemTextColor
?attr/colorOnSurfaceVariant
Color (active) app:itemTextColor setItemTextColor
getItemTextColor
?attr/colorOnSurface
Typography (inactive) app:itemTextAppearanceInactive
app:horizontalItemTextAppearanceInactive
setItemTextAppearanceInactive
getItemTextAppearanceInactive
setHorizontalItemTextAppearanceInactive
getHorizontalItemTextAppearanceInactive
?attr/textAppearanceTitleSmall for regular item configuration, ?attr/textAppearanceLabelLarge for horizontal
Typography (active) app:itemTextAppearanceActive
app:horizontalItemTextAppearanceActive
setItemTextAppearanceActive
getItemTextAppearanceActive
setHorizontalItemTextAppearanceActive
getHorizontalItemTextAppearanceActive
?attr/textAppearanceTitleSmall for regular item configuration, ?attr/textAppearanceLabelLarge for horizontal
Typography (active) app:itemTextAppearanceActiveBoldEnabled setItemTextAppearanceActiveBoldEnabled true
Max lines app:labelMaxLines setLabelMaxLines
getLabelMaxLines
1
Scale with font size app:scaleLabelWithFontSize setScaleLabelTextWithFont
getScaleLabelTextWithFont
false

Styles

Element Style Container color Icon/Text label color (inactive) Icon/Text label color (active)
Default style Widget.Material3.NavigationRailView ?attr/colorSurface ?attr/colorOnSurfaceVariant ?attr/colorOnSurface
?attr/colorOnSecondaryContainer

Default style theme attribute: ?attr/navigationRailStyle

See the full list of styles, navigation bar attributes, and navigation rail attributes.

Theming a navigation rail

Navigation rail supports Material Theming, which can customize color and typography.

API and source code:

The following example shows a navigation rail with Material Theming.

Navigation rail theming example

Implementing navigation rail theming

Use theme attributes and a style in res/values/styles.xml which apply to all navigation rails and affect other components:

<style name="Theme.App" parent="Theme.Material3.*">
    ...
    <item name="colorPrimary">@color/shrine_theme_light_primary</item>
    <item name="colorSecondaryContainer">@color/shrine_theme_light_secondaryContainer</item>
    <item name="colorOnSecondaryContainer">@color/shrine_theme_light_onSecondaryContainer</item>
    <item name="colorTertiaryContainer">@color/shrine_theme_light_tertiaryContainer</item>
    <item name="colorOnTertiaryContainer">@color/shrine_theme_light_onTertiaryContainer</item>
    <item name="colorError">@color/shrine_theme_light_error</item>
    <item name="colorErrorContainer">@color/shrine_theme_light_errorContainer</item>
    <item name="colorOnError">@color/shrine_theme_light_onError</item>
    <item name="colorOnErrorContainer">@color/shrine_theme_light_onErrorContainer</item>
    <item name="colorSurface">@color/shrine_theme_light_surface</item>
    <item name="colorOnSurface">@color/shrine_theme_light_onSurface</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 rails but do not affect other components:

<style name="Theme.App" parent="Theme.Material3.*">
    ...
    <item name="navigationRailStyle">@style/Widget.App.NavigationRailView</item>
</style>

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

<style name="ThemeOverlay.App.NavigationRailView" parent="">
    <item name="colorPrimary">@color/shrine_theme_light_primary</item>
    <item name="colorSecondaryContainer">@color/shrine_theme_light_secondaryContainer</item>
    <item name="colorOnSecondaryContainer">@color/shrine_theme_light_onSecondaryContainer</item>
    <item name="colorTertiaryContainer">@color/shrine_theme_light_tertiaryContainer</item>
    <item name="colorOnTertiaryContainer">@color/shrine_theme_light_onTertiaryContainer</item>
    <item name="colorError">@color/shrine_theme_light_error</item>
    <item name="colorErrorContainer">@color/shrine_theme_light_errorContainer</item>
    <item name="colorOnError">@color/shrine_theme_light_onError</item>
    <item name="colorOnErrorContainer">@color/shrine_theme_light_onErrorContainer</item>
    <item name="colorSurface">@color/shrine_theme_light_surface</item>
    <item name="colorOnSurface">@color/shrine_theme_light_onSurface</item>
    <item name="colorOnSurfaceVariant">@color/shrine_theme_light_onSurfaceVariant</item>
</style>

Or use the style in the layout, which affects only this specific navigation rail bar:

<com.google.android.material.navigationrail.NavigationRailView
    ...
    style="@style/Widget.App.NavigationRailView"
/>