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