1 /* <lambda>null2 * Copyright 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 @file:Suppress("DEPRECATION") // Suppress for WindowWidthSizeClass 18 19 package androidx.compose.material3.demos 20 21 import androidx.compose.foundation.layout.Column 22 import androidx.compose.foundation.layout.Spacer 23 import androidx.compose.foundation.layout.fillMaxWidth 24 import androidx.compose.foundation.layout.padding 25 import androidx.compose.material.icons.Icons 26 import androidx.compose.material.icons.automirrored.filled.MenuOpen 27 import androidx.compose.material.icons.filled.Add 28 import androidx.compose.material.icons.filled.Favorite 29 import androidx.compose.material.icons.filled.Menu 30 import androidx.compose.material.icons.outlined.FavoriteBorder 31 import androidx.compose.material3.Button 32 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi 33 import androidx.compose.material3.ExtendedFloatingActionButton 34 import androidx.compose.material3.Icon 35 import androidx.compose.material3.IconButton 36 import androidx.compose.material3.MaterialExpressiveTheme 37 import androidx.compose.material3.ModalWideNavigationRail 38 import androidx.compose.material3.Surface 39 import androidx.compose.material3.Text 40 import androidx.compose.material3.WideNavigationRailItem 41 import androidx.compose.material3.WideNavigationRailValue 42 import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo 43 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuite 44 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItem 45 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults 46 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldLayout 47 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldValue 48 import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType 49 import androidx.compose.material3.adaptive.navigationsuite.rememberNavigationSuiteScaffoldState 50 import androidx.compose.material3.animateFloatingActionButton 51 import androidx.compose.material3.rememberWideNavigationRailState 52 import androidx.compose.runtime.Composable 53 import androidx.compose.runtime.getValue 54 import androidx.compose.runtime.mutableIntStateOf 55 import androidx.compose.runtime.remember 56 import androidx.compose.runtime.rememberCoroutineScope 57 import androidx.compose.runtime.setValue 58 import androidx.compose.ui.Alignment 59 import androidx.compose.ui.Modifier 60 import androidx.compose.ui.semantics.semantics 61 import androidx.compose.ui.semantics.stateDescription 62 import androidx.compose.ui.text.style.TextAlign 63 import androidx.compose.ui.unit.dp 64 import androidx.window.core.layout.WindowHeightSizeClass 65 import androidx.window.core.layout.WindowWidthSizeClass 66 import kotlinx.coroutines.launch 67 68 @OptIn(ExperimentalMaterial3ExpressiveApi::class) 69 @Composable 70 @Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated 71 fun NavigationSuiteScaffoldCustomConfigDemo() { 72 var selectedItem by remember { mutableIntStateOf(0) } 73 val navItems = listOf("Songs", "Artists", "Playlists") 74 // Custom configuration that shows a wide navigation rail in small/medium width screens, an 75 // expanded wide navigation rail in expanded width screens, and a short navigation bar in small 76 // height screens. 77 val navSuiteType = 78 with(currentWindowAdaptiveInfo()) { 79 if ( 80 windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || 81 windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.MEDIUM 82 ) { 83 NavigationSuiteType.WideNavigationRailCollapsed 84 } else if (windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT) { 85 NavigationSuiteType.ShortNavigationBarMedium 86 } else if (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { 87 NavigationSuiteType.WideNavigationRailExpanded 88 } else { 89 NavigationSuiteScaffoldDefaults.navigationSuiteType(currentWindowAdaptiveInfo()) 90 } 91 } 92 val state = rememberNavigationSuiteScaffoldState() 93 val scope = rememberCoroutineScope() 94 val railState = rememberWideNavigationRailState() 95 val railExpanded = railState.currentValue == WideNavigationRailValue.Expanded 96 val isWideNavRailCollapsedType = navSuiteType == NavigationSuiteType.WideNavigationRailCollapsed 97 val animateFAB = 98 if ( 99 navSuiteType == NavigationSuiteType.ShortNavigationBarMedium || 100 navSuiteType == NavigationSuiteType.NavigationBar 101 ) { 102 Modifier.animateFloatingActionButton( 103 visible = state.currentValue == NavigationSuiteScaffoldValue.Visible, 104 alignment = Alignment.BottomEnd 105 ) 106 } else { 107 Modifier 108 } 109 val fab = 110 @Composable { 111 val startPadding = 112 if (navSuiteType == NavigationSuiteType.ShortNavigationBarMedium) { 113 0.dp 114 } else { 115 24.dp 116 } 117 ExtendedFloatingActionButton( 118 modifier = Modifier.padding(start = startPadding).then(animateFAB), 119 onClick = { /* onClick function for FAB. */ }, 120 expanded = 121 if (isWideNavRailCollapsedType) railExpanded 122 else navSuiteType == NavigationSuiteType.WideNavigationRailExpanded, 123 icon = { Icon(Icons.Filled.Add, "FAB") }, 124 text = { Text("Add new") } 125 ) 126 } 127 val menuButton = 128 @Composable { 129 IconButton( 130 modifier = 131 Modifier.padding(start = 24.dp, bottom = 8.dp).semantics { 132 stateDescription = if (railExpanded) "Expanded" else "Collapsed" 133 }, 134 onClick = { scope.launch { railState.toggle() } } 135 ) { 136 if (railExpanded) { 137 Icon(Icons.AutoMirrored.Filled.MenuOpen, "Collapse rail") 138 } else { 139 Icon(Icons.Filled.Menu, "Expand rail") 140 } 141 } 142 } 143 144 MaterialExpressiveTheme { 145 Surface { 146 // Use NavigationSuiteScaffoldLayout so that we can customize the NavigationSuite. 147 NavigationSuiteScaffoldLayout( 148 navigationSuiteType = navSuiteType, 149 state = state, 150 primaryActionContent = fab, 151 navigationSuite = { 152 // Pass in a custom modal rail to substitute the default collapsed wide nav 153 // rail. 154 if (isWideNavRailCollapsedType) { 155 ModalWideNavigationRail( 156 state = railState, 157 header = { 158 Column { 159 menuButton() 160 Spacer(Modifier.padding(vertical = 8.dp)) 161 fab() 162 } 163 }, 164 expandedHeaderTopPadding = 64.dp, 165 ) { 166 navItems.forEachIndexed { index, navItem -> 167 WideNavigationRailItem( 168 icon = { 169 Icon( 170 if (selectedItem == index) Icons.Filled.Favorite 171 else Icons.Outlined.FavoriteBorder, 172 contentDescription = null 173 ) 174 }, 175 label = { Text(navItem) }, 176 selected = selectedItem == index, 177 onClick = { selectedItem = index }, 178 railExpanded = railExpanded, 179 ) 180 } 181 } 182 } else { 183 NavigationSuite( 184 navigationSuiteType = navSuiteType, 185 primaryActionContent = fab, 186 ) { 187 navItems.forEachIndexed { index, navItem -> 188 NavigationSuiteItem( 189 navigationSuiteType = navSuiteType, 190 icon = { 191 Icon( 192 if (selectedItem == index) Icons.Filled.Favorite 193 else Icons.Outlined.FavoriteBorder, 194 contentDescription = null 195 ) 196 }, 197 label = { Text(navItem) }, 198 selected = selectedItem == index, 199 onClick = { selectedItem = index }, 200 ) 201 } 202 } 203 } 204 } 205 ) { 206 // Screen content. 207 Column( 208 modifier = Modifier.fillMaxWidth(), 209 horizontalAlignment = Alignment.CenterHorizontally 210 ) { 211 Text( 212 modifier = Modifier.padding(16.dp), 213 text = 214 "Current NavigationSuiteType: $navSuiteType\n" + 215 "Visibility: ${state.currentValue}", 216 textAlign = TextAlign.Center 217 ) 218 Button(onClick = { scope.launch { state.toggle() } }) { 219 Text("Hide/show navigation component") 220 } 221 } 222 } 223 } 224 } 225 } 226