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>
Note: NavigationRailView
displays three to no more than seven app
destinations, and can include a header view. Each destination is represented by
an icon and a text label.
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
.
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 items
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.
<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. |
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 is set:
badge.number = 99
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()
}
To remove any BadgeDrawable
s that are no longer needed:
navigationRail.removeBadge(menuItemId)
See the BadgeDrawable
documentation for more information.
API and source code:
NavigationRailView
The following example shows a navigation rail with four icons:
- Alarms
- Schedule
- Timers
- Stopwatch
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:
- Container
- Header - menu icon (optional)
- Header - Floating action button (optional)
- Icon - active
- Active indicator
- Label text - active (optional)
- Icon - inactive
- Label text - inactive (optional)
- Large badge (optional)
- Large badge label (optional)
- Badge (optional)
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 |
Element | Attribute | Related methods | Default value |
---|---|---|---|
Header view | app:headerLayout |
addHeaderView removeHeaderView getHeaderView |
N/A |
See the FAB documentation for more attributes.
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 |
Element | Attribute | Related methods | Default value |
---|---|---|---|
Color | android:color |
setItemActiveIndicatorColor getItemActiveIndicatorColor |
?attr/colorSecondaryContainer |
Width | android:width |
setItemActiveIndicatorWidth getItemActiveIndicatorWidth |
56dp |
Height | android:height |
setItemActiveIndicatorHeight setItemActiveIndicatorHeight |
32dp |
Shape | app:shapeAppearance |
setItemActiveIndicatorShapeAppearance getItemActiveIndicatorShapeAppearance |
50% rounded |
Margin horizontal | app:marginHorizontal |
setItemActiveIndicatorMarginHorizontal getItemActiveIndicatorMarginHorizontal |
4dp |
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 |
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 |
setItemTextAppearanceInactive getItemTextAppearanceInactive |
?attr/textAppearanceTitleSmall |
Typography (active) | app:itemTextAppearanceActive |
setItemTextAppearanceActive getItemTextAppearanceActive |
?attr/textAppearanceTitleSmall |
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"
/>