ModalWideNavigationRail

Functions summary

Unit
@Composable
ModalWideNavigationRail(
    modifier: Modifier,
    state: WideNavigationRailState,
    hideOnCollapse: Boolean,
    collapsedShape: Shape,
    expandedShape: Shape,
    colors: WideNavigationRailColors,
    header: (@Composable () -> Unit)?,
    expandedHeaderTopPadding: Dp,
    windowInsets: WindowInsets,
    arrangement: Arrangement.Vertical,
    expandedProperties: ModalWideNavigationRailProperties,
    contentPadding: PaddingValues,
    content: @Composable () -> Unit
)

Material design modal wide navigation rail.

Cmn

Functions

ModalWideNavigationRail

@Composable
fun ModalWideNavigationRail(
    modifier: Modifier = Modifier,
    state: WideNavigationRailState = rememberWideNavigationRailState(),
    hideOnCollapse: Boolean = false,
    collapsedShape: Shape = WideNavigationRailDefaults.modalCollapsedShape,
    expandedShape: Shape = WideNavigationRailDefaults.modalExpandedShape,
    colors: WideNavigationRailColors = WideNavigationRailDefaults.colors(),
    header: (@Composable () -> Unit)? = null,
    expandedHeaderTopPadding: Dp = 0.dp,
    windowInsets: WindowInsets = WideNavigationRailDefaults.windowInsets,
    arrangement: Arrangement.Vertical = WideNavigationRailDefaults.arrangement,
    expandedProperties: ModalWideNavigationRailProperties = WideNavigationRailDefaults.ModalExpandedProperties,
    contentPadding: PaddingValues = WideNavigationRailDefaults.ContentPadding,
    content: @Composable () -> Unit
): Unit

Material design modal wide navigation rail.

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

The modal 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 ModalWideNavigationRail when collapsed behaves like a collapsed WideNavigationRail. When expanded, the modal wide navigation rail blocks interaction with the rest of an app’s content with a scrim. It is elevated above the app’s UI and doesn't affect the screen’s layout grid. That can be achieved 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.ModalWideNavigationRail
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()) {
    ModalWideNavigationRail(
        state = state,
        // Note: the value of expandedHeaderTopPadding depends on the layout of your screen in
        // order to achieve the best alignment.
        expandedHeaderTopPadding = 64.dp,
        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 = {
                    Icon(
                        if (selectedItem == index) selectedIcons[index]
                        else unselectedIcons[index],
                        contentDescription = item,
                    )
                },
                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 = "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 dismissible ModalWideNavigationRail, that enters from offscreen instead of expanding from the collapsed rail, set hideOnCollapse to true. That can be achieved like so:

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalWideNavigationRail
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.rememberWideNavigationRailState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
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()

Row(Modifier.fillMaxSize()) {
    ModalWideNavigationRail(state = state, hideOnCollapse = 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
                    scope.launch { state.collapse() }
                },
            )
        }
    }

    Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
        val currentPage = items.get(selectedItem)
        Button(onClick = { scope.launch { state.expand() } }, Modifier.padding(32.dp)) {
            Text(text = "$currentPage Page\nOpen modal rail", textAlign = TextAlign.Center)
        }
    }
}

See WideNavigationRailItem for configuration specific to each item, and not the overall ModalWideNavigationRail 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

hideOnCollapse: Boolean = false

whether this wide navigation rail should slide offscreen when it collapses and be hidden, or stay on screen as a collapsed wide navigation rail (default)

collapsedShape: Shape = WideNavigationRailDefaults.modalCollapsedShape

the shape of this wide navigation rail's container when it's collapsed

expandedShape: Shape = WideNavigationRailDefaults.modalExpandedShape

the shape of this wide navigation rail's container when it's expanded

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

expandedHeaderTopPadding: Dp = 0.dp

the padding to be applied to the top of the rail. It's usually needed in order to align the content of the rail between the collapsed and expanded animation

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

expandedProperties: ModalWideNavigationRailProperties = WideNavigationRailDefaults.ModalExpandedProperties

ModalWideNavigationRailProperties for further customization of the expanded modal wide navigation rail's window behavior

contentPadding: PaddingValues = WideNavigationRailDefaults.ContentPadding

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

content: @Composable () -> Unit

the content of this modal wide navigation rail, usually WideNavigationRailItems