0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Material 3 Expressive実装例 : Navigation rail

Last updated at Posted at 2025-06-11

Material 3 Expressive の実装例の紹介シリーズです。
今回は Navigation rail を紹介します。
(androidx.compose.material3:material3:1.4.0-alpha15 時点での内容になります。)

Material 3 Expressive での変更点

  • これまでの Navigaton rail は非推奨になり、Collapsed と Expanded の 2 パターンが追加され、Collapsed と Expanded の切り替えが可能
    • これまでの Navigation rail の置き換え先には Collapsed
    • Navigation drawer の置き換え先として Expanded
  • Expanded な Navigation rail には Non-modal と Modal の 2 パターン
  • 選択中のアイテムのラベルの色が On surface variant から Secondary に変更

実装

Collapsed な Navigation rail

Material 3 Expressive では NavigationRail ではなくて WideNavigationRail を使用します。

WideNavigationRail {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            icon = {
                Icon(
                    painter = painterResource(item.icon),
                    contentDescription = null
                )
            },
            label = { Text(item.label) },
            selected = selectedItem == index,
            onClick = { selectedItem = index }
        )
    }
}

Expanded で Non-modal な Navigation rail

WideNavigationRailStateWideNavigationRailValue.Expanded を設定しつつ、WideNavigationRailItem にも railExpanded = true を設定することで Expanded な Navigation rail が表示できます。

WideNavigationRail(
    state = rememberWideNavigationRailState(initialValue = WideNavigationRailValue.Expanded)
) {
    items.forEachIndexed { index, item ->
        WideNavigationRailItem(
            railExpanded = true,
            icon = {
                Icon(
                    painter = painterResource(item.icon),
                    contentDescription = null
                )
            },
            label = { Text(item.label) },
            selected = selectedItem == index,
            onClick = { selectedItem = index }
        )
    }
}

Collapsed と Expanded のシームレスな切り替え

WideNavigationRailStatecollapse() / expand() を呼び出すことで Navigation rail のシームレスな開閉を実装することができます。

val state = rememberWideNavigationRailState()
val scope = rememberCoroutineScope()

Row {
    WideNavigationRail(
        state = state,
        header = {
            IconButton(
                modifier = Modifier
                    .padding(start = 24.dp)
                    .semantics {
                        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(painterResource(R.drawable.ic_menu_open), "Collapse rail")
                } else {
                    Icon(painterResource(R.drawable.ic_menu), "Expand rail")
                }
            }
        }
    ) {
        items.forEachIndexed { index, item ->
            WideNavigationRailItem(
                railExpanded = state.targetValue == WideNavigationRailValue.Expanded,
                icon = {
                    Icon(
                        painter = painterResource(item.icon),
                        contentDescription = null
                    )
                },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
    ...
}

Modal な Navigation rail

Modal な Navigation rail を使う時は WideNavigationRail の代わりに ModalWideNavigationRail を使用します。

val state = rememberWideNavigationRailState()
val scope = rememberCoroutineScope()

Row {
    ModalWideNavigationRail(
        state = state,
        header = {
            IconButton(
                modifier = Modifier
                    .padding(start = 24.dp)
                    .semantics {
                        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(painterResource(R.drawable.ic_menu_open), "Collapse rail")
                } else {
                    Icon(painterResource(R.drawable.ic_menu), "Expand rail")
                }
            }
        }
    ) {
        items.forEachIndexed { index, item ->
            WideNavigationRailItem(
                railExpanded = state.targetValue == WideNavigationRailValue.Expanded,
                icon = {
                    Icon(
                        painter = painterResource(item.icon),
                        contentDescription = null
                    )
                },
                label = { Text(item.label) },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
    ...
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?