Navigation rail provides access to primary destinations in your app on tablet and desktop screens.
Contents
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:
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:
NavigationRailView
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:
LABEL_VISIBILITY_AUTO
(default): The label behaves as “labeled” when there
are 3 items or less, or “selected” when there are 4 items or moreLABEL_VISIBILITY_SELECTED
: The label is only shown on the selected
navigation itemLABEL_VISIBILITY_LABELED
: The label is shown on all navigation itemsLABEL_VISIBILITY_UNLABELED
: The label is hidden for all navigation itemsThe 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.
<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. |
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 |
---|---|
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.
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 BadgeDrawable
s 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
The following is an anatomy diagram for the navigation rail:
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 |
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.
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
.
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 |
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 |
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.
Navigation rail supports Material Theming, which can customize color and typography.
API and source code:
NavigationRailView
The following example shows a navigation rail with Material 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"
/>