We just launched Compose Examples featuring over 150+ components! Check it out →

WideNavigationRail

Common

Component in Material 3 Compose

Material design wide navigation rail.

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

The wide navigation rail should be used to display multiple [WideNavigationRailItem]s, 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.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.material3:material3:1.4.0-alpha01")
}

Overloads

@ExperimentalMaterial3ExpressiveApi
@Composable
fun WideNavigationRail(
    modifier: Modifier = Modifier,
    expanded: Boolean = false,
    shape: Shape = WideNavigationRailDefaults.containerShape,
    colors: WideNavigationRailColors = WideNavigationRailDefaults.colors(),
    header: @Composable (() -> Unit)? = null,
    windowInsets: WindowInsets = WideNavigationRailDefaults.windowInsets,
    arrangement: WideNavigationRailArrangement = WideNavigationRailDefaults.Arrangement,
    content: @Composable () -> Unit
)

Parameters

namedescription
modifierthe [Modifier] to be applied to this wide navigation rail
expandedwhether this wide navigation rail is expanded or collapsed (default).
shapedefines the shape of this wide navigation rail's container.
colors[WideNavigationRailColors] that will be used to resolve the colors used for this wide navigation rail. See [WideNavigationRailDefaults.colors]
headeroptional header that may hold a [FloatingActionButton] or a logo
windowInsetsa window insets of the wide navigation rail
arrangementthe [WideNavigationRailArrangement] of this wide navigation rail
contentthe content of this wide navigation rail, typically [WideNavigationRailItem]s

Code Examples

WideNavigationRailCollapsedSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun WideNavigationRailCollapsedSample() {
    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(
                icon = {
                    Icon(
                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
                        contentDescription = null
                    )
                },
                label = { Text(item) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
}

WideNavigationRailExpandedSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun WideNavigationRailExpandedSample() {
    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(expanded = true) {
        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 }
            )
        }
    }
}

WideNavigationRailResponsiveSample

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun WideNavigationRailResponsiveSample() {
    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)
    var expanded by remember { mutableStateOf(false) }

    Row(Modifier.fillMaxWidth()) {
        WideNavigationRail(
            expanded = expanded,
            header = {
                IconButton(
                    modifier =
                        Modifier.padding(start = 24.dp).semantics {
                            // The button must announce the expanded or collapsed state of the rail
                            // for accessibility.
                            stateDescription = if (expanded) "Expanded" else "Collapsed"
                        },
                    onClick = { expanded = !expanded }
                ) {
                    if (expanded) Icon(Icons.AutoMirrored.Filled.MenuOpen, "Collapse rail")
                    else Icon(Icons.Filled.Menu, "Expand rail")
                }
            }
        ) {
            items.forEachIndexed { index, item ->
                WideNavigationRailItem(
                    railExpanded = expanded,
                    icon = {
                        Icon(
                            if (selectedItem == index) selectedIcons[index]
                            else unselectedIcons[index],
                            contentDescription = null
                        )
                    },
                    label = { Text(item) },
                    selected = selectedItem == index,
                    onClick = { selectedItem = index }
                )
            }
        }

        val textString = if (expanded) "expanded" else "collapsed"
        Column {
            Text(modifier = Modifier.padding(16.dp), text = "The rail is $textString.")
            Text(
                modifier = Modifier.padding(16.dp),
                text =
                    "Note: The orientation of this demo has been locked to portrait mode, because" +
                        " landscape mode may result in a compact height in certain devices. For" +
                        " any compact screen dimensions, use a Navigation Bar instead."
            )
        }
    }

    // Lock the orientation for this demo as the navigation rail may look cut off in landscape in
    // smaller screens.
    val context = LocalContext.current
    DisposableEffect(context) {
        (context as? Activity)?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        onDispose {
            (context as? Activity)?.requestedOrientation =
                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
        }
    }
}
by @alexstyl