WideNavigationRail

Functions summary

Unit
@Composable
WideNavigationRail(
    modifier: Modifier,
    state: WideNavigationRailState,
    shape: Shape,
    colors: WideNavigationRailColors,
    header: (@Composable () -> Unit)?,
    windowInsets: WindowInsets,
    arrangement: Arrangement.Vertical,
    contentPadding: PaddingValues,
    content: @Composable () -> Unit
)

Material design wide navigation rail.

Cmn

Functions

WideNavigationRail

@Composable
fun WideNavigationRail(
    modifier: Modifier = Modifier,
    state: WideNavigationRailState = rememberWideNavigationRailState(),
    shape: Shape = WideNavigationRailDefaults.shape,
    colors: WideNavigationRailColors = WideNavigationRailDefaults.colors(),
    header: (@Composable () -> Unit)? = null,
    windowInsets: WindowInsets = WideNavigationRailDefaults.windowInsets,
    arrangement: Arrangement.Vertical = WideNavigationRailDefaults.arrangement,
    contentPadding: PaddingValues = WideNavigationRailDefaults.ContentPadding,
    content: @Composable () -> Unit
): Unit

Material design wide navigation rail.

Wide navigation rails provide access to primary destinations in apps when using tablet and desktop screens.

Wide navigation rail collapsed
image

Wide navigation rail expanded
image

The wide navigation rail should be used to display multiple WideNavigationRailItems, each representing a singular app destination, and, optionally, a header containing a menu button, a FloatingActionButton, and/or a logo. Each destination is typically represented by an icon and a text label.

The WideNavigationRail is collapsed by default, but it also supports being expanded via a WideNavigationRailState. When collapsed, the rail should display three to seven navigation items. A simple example looks like:

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.StarBorder
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Text
import androidx.compose.material3.WideNavigationRail
import androidx.compose.material3.WideNavigationRailItem
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember

var selectedItem by remember { mutableIntStateOf(0) }
val items = listOf("Home", "Search", "Settings")
val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
val unselectedIcons =
    listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
WideNavigationRail {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            railExpanded = false,
            icon = {
                Icon(
                    if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
                    contentDescription = null,
                )
            },
            label = { Text(item) },
            selected = selectedItem == index,
            onClick = { selectedItem = index },
        )
    }
}

When expanded, the rail should display at least three navigation items. A simple example looks like:

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.StarBorder
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Text
import androidx.compose.material3.WideNavigationRail
import androidx.compose.material3.WideNavigationRailItem
import androidx.compose.material3.WideNavigationRailValue
import androidx.compose.material3.rememberWideNavigationRailState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember

var selectedItem by remember { mutableIntStateOf(0) }
val items = listOf("Home", "Search", "Settings")
val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
val unselectedIcons =
    listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
WideNavigationRail(
    state = rememberWideNavigationRailState(initialValue = WideNavigationRailValue.Expanded)
) {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            railExpanded = true,
            icon = {
                Icon(
                    if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
                    contentDescription = null,
                )
            },
            label = { Text(item) },
            selected = selectedItem == index,
            onClick = { selectedItem = index },
        )
    }
}

The WideNavigationRail also supports automatically animating between the collapsed and expanded values. That can be done like so:

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.MenuOpen
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.StarBorder
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.WideNavigationRail
import androidx.compose.material3.WideNavigationRailItem
import androidx.compose.material3.WideNavigationRailValue
import androidx.compose.material3.rememberTooltipState
import androidx.compose.material3.rememberWideNavigationRailState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp

var selectedItem by remember { mutableIntStateOf(0) }
val items = listOf("Home", "Search", "Settings")
val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
val unselectedIcons =
    listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
val state = rememberWideNavigationRailState()
val scope = rememberCoroutineScope()
val headerDescription =
    if (state.targetValue == WideNavigationRailValue.Expanded) {
        "Collapse rail"
    } else {
        "Expand rail"
    }

Row(Modifier.fillMaxWidth()) {
    WideNavigationRail(
        state = state,
        header = {
            // Header icon button should have a tooltip.
            TooltipBox(
                positionProvider =
                    TooltipDefaults.rememberTooltipPositionProvider(
                        TooltipAnchorPosition.Above
                    ),
                tooltip = { PlainTooltip { Text(headerDescription) } },
                state = rememberTooltipState(),
            ) {
                IconButton(
                    modifier =
                        Modifier.padding(start = 24.dp).semantics {
                            // The button must announce the expanded or collapsed state of the
                            // rail for accessibility.
                            stateDescription =
                                if (state.currentValue == WideNavigationRailValue.Expanded) {
                                    "Expanded"
                                } else {
                                    "Collapsed"
                                }
                        },
                    onClick = {
                        scope.launch {
                            if (state.targetValue == WideNavigationRailValue.Expanded)
                                state.collapse()
                            else state.expand()
                        }
                    },
                ) {
                    if (state.targetValue == WideNavigationRailValue.Expanded) {
                        Icon(Icons.AutoMirrored.Filled.MenuOpen, headerDescription)
                    } else {
                        Icon(Icons.Filled.Menu, headerDescription)
                    }
                }
            }
        },
    ) {
        items.forEachIndexed { index, item ->
            WideNavigationRailItem(
                railExpanded = state.targetValue == WideNavigationRailValue.Expanded,
                icon = {
                    val imageVector =
                        if (selectedItem == index) {
                            selectedIcons[index]
                        } else {
                            unselectedIcons[index]
                        }
                    Icon(imageVector = imageVector, contentDescription = null)
                },
                label = { Text(item) },
                selected = selectedItem == index,
                onClick = { selectedItem = index },
            )
        }
    }

    val textString =
        if (state.currentValue == WideNavigationRailValue.Expanded) {
            "Expanded"
        } else {
            "Collapsed"
        }
    Column {
        Text(modifier = Modifier.padding(16.dp), text = "Is animating: " + state.isAnimating)
        Text(modifier = Modifier.padding(16.dp), text = "The rail is $textString.")
        Text(
            modifier = Modifier.padding(16.dp),
            text =
                "Note: This demo is best shown in portrait mode, as landscape mode" +
                    " may result in a compact height in certain devices. For any" +
                    " compact screen dimensions, use a Navigation Bar instead.",
        )
    }
}

For a modal variation of the wide navigation rail, see ModalWideNavigationRail.

Finally, the WideNavigationRail supports setting an Arrangement.Vertical for the items, with Arrangement.Top being the default. The header will always be at the top.

See WideNavigationRailItem for configuration specific to each item, and not the overall WideNavigationRail component.

Parameters
modifier: Modifier = Modifier

the Modifier to be applied to this wide navigation rail

state: WideNavigationRailState = rememberWideNavigationRailState()

the WideNavigationRailState of this wide navigation rail

shape: Shape = WideNavigationRailDefaults.shape

defines the shape of this wide navigation rail's container.

colors: WideNavigationRailColors = WideNavigationRailDefaults.colors()

WideNavigationRailColors that will be used to resolve the colors used for this wide navigation rail. See WideNavigationRailDefaults.colors

header: (@Composable () -> Unit)? = null

optional header that may hold a FloatingActionButton or a logo

windowInsets: WindowInsets = WideNavigationRailDefaults.windowInsets

a window insets of the wide navigation rail

arrangement: Arrangement.Vertical = WideNavigationRailDefaults.arrangement

the Arrangement.Vertical of this wide navigation rail for its content. Note that if there's a header present, the items will be arranged on the remaining space below it, except for the center arrangement which considers the entire height of the container

contentPadding: PaddingValues = WideNavigationRailDefaults.ContentPadding

the spacing values to apply internally between the container and the content

content: @Composable () -> Unit

the content of this wide navigation rail, typically WideNavigationRailItems