1 /*
<lambda>null2 * Copyright 2023 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 imports of WindowWidthSizeClass
18
19 package androidx.compose.material3.adaptive.navigationsuite
20
21 import androidx.compose.animation.core.Animatable
22 import androidx.compose.animation.core.SpringSpec
23 import androidx.compose.animation.core.VectorConverter
24 import androidx.compose.animation.core.animateFloatAsState
25 import androidx.compose.animation.core.spring
26 import androidx.compose.foundation.interaction.Interaction
27 import androidx.compose.foundation.interaction.MutableInteractionSource
28 import androidx.compose.foundation.layout.Arrangement
29 import androidx.compose.foundation.layout.Box
30 import androidx.compose.foundation.layout.Spacer
31 import androidx.compose.foundation.layout.WindowInsets
32 import androidx.compose.foundation.layout.WindowInsetsSides
33 import androidx.compose.foundation.layout.consumeWindowInsets
34 import androidx.compose.foundation.layout.heightIn
35 import androidx.compose.foundation.layout.only
36 import androidx.compose.foundation.layout.padding
37 import androidx.compose.material3.BadgedBox
38 import androidx.compose.material3.DrawerDefaults
39 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
40 import androidx.compose.material3.Icon
41 import androidx.compose.material3.LocalContentColor
42 import androidx.compose.material3.MaterialTheme
43 import androidx.compose.material3.NavigationBar
44 import androidx.compose.material3.NavigationBarDefaults
45 import androidx.compose.material3.NavigationBarItem
46 import androidx.compose.material3.NavigationBarItemColors
47 import androidx.compose.material3.NavigationBarItemDefaults
48 import androidx.compose.material3.NavigationDrawerItem
49 import androidx.compose.material3.NavigationDrawerItemColors
50 import androidx.compose.material3.NavigationDrawerItemDefaults
51 import androidx.compose.material3.NavigationItemColors
52 import androidx.compose.material3.NavigationItemIconPosition
53 import androidx.compose.material3.NavigationRail
54 import androidx.compose.material3.NavigationRailDefaults
55 import androidx.compose.material3.NavigationRailItem
56 import androidx.compose.material3.NavigationRailItemColors
57 import androidx.compose.material3.NavigationRailItemDefaults
58 import androidx.compose.material3.PermanentDrawerSheet
59 import androidx.compose.material3.ShortNavigationBar
60 import androidx.compose.material3.ShortNavigationBarDefaults
61 import androidx.compose.material3.ShortNavigationBarItem
62 import androidx.compose.material3.ShortNavigationBarItemDefaults
63 import androidx.compose.material3.Surface
64 import androidx.compose.material3.Text
65 import androidx.compose.material3.WideNavigationRail
66 import androidx.compose.material3.WideNavigationRailColors
67 import androidx.compose.material3.WideNavigationRailDefaults
68 import androidx.compose.material3.WideNavigationRailItem
69 import androidx.compose.material3.WideNavigationRailItemDefaults
70 import androidx.compose.material3.WideNavigationRailValue
71 import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi
72 import androidx.compose.material3.adaptive.WindowAdaptiveInfo
73 import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
74 import androidx.compose.material3.contentColorFor
75 import androidx.compose.material3.rememberWideNavigationRailState
76 import androidx.compose.runtime.Composable
77 import androidx.compose.runtime.ProvidableCompositionLocal
78 import androidx.compose.runtime.Stable
79 import androidx.compose.runtime.State
80 import androidx.compose.runtime.collection.MutableVector
81 import androidx.compose.runtime.collection.mutableVectorOf
82 import androidx.compose.runtime.compositionLocalOf
83 import androidx.compose.runtime.derivedStateOf
84 import androidx.compose.runtime.getValue
85 import androidx.compose.runtime.movableContentOf
86 import androidx.compose.runtime.remember
87 import androidx.compose.runtime.rememberUpdatedState
88 import androidx.compose.runtime.saveable.Saver
89 import androidx.compose.runtime.saveable.rememberSaveable
90 import androidx.compose.ui.Alignment
91 import androidx.compose.ui.Modifier
92 import androidx.compose.ui.graphics.Color
93 import androidx.compose.ui.layout.Layout
94 import androidx.compose.ui.layout.layoutId
95 import androidx.compose.ui.unit.dp
96 import androidx.compose.ui.util.fastFirst
97 import androidx.window.core.layout.WindowHeightSizeClass
98 import androidx.window.core.layout.WindowWidthSizeClass
99
100 /** Possible values of [NavigationSuiteScaffoldState]. */
101 enum class NavigationSuiteScaffoldValue {
102 /** The state of the navigation component of the scaffold when it's visible. */
103 Visible,
104
105 /** The state of the navigation component of the scaffold when it's hidden. */
106 Hidden
107 }
108
109 /**
110 * A state object that can be hoisted to observe the navigation suite scaffold state. It allows for
111 * setting its navigation component to be hidden or displayed.
112 *
113 * @see rememberNavigationSuiteScaffoldState to construct the default implementation.
114 */
115 @Stable
116 interface NavigationSuiteScaffoldState {
117 /** Whether the state is currently animating. */
118 val isAnimating: Boolean
119
120 /** Whether the navigation component is going to be shown or hidden. */
121 val targetValue: NavigationSuiteScaffoldValue
122
123 /** Whether the navigation component is currently shown or hidden. */
124 val currentValue: NavigationSuiteScaffoldValue
125
126 /** Hide the navigation component with animation and suspend until it fully expands. */
hidenull127 suspend fun hide()
128
129 /** Show the navigation component with animation and suspend until it fully expands. */
130 suspend fun show()
131
132 /**
133 * Hide the navigation component with animation if it's shown, or collapse it otherwise, and
134 * suspend until it fully expands.
135 */
136 suspend fun toggle()
137
138 /**
139 * Set the state without any animation and suspend until it's set.
140 *
141 * @param targetValue the value to set to
142 */
143 suspend fun snapTo(targetValue: NavigationSuiteScaffoldValue)
144 }
145
146 /** Create and [remember] a [NavigationSuiteScaffoldState] */
147 @Composable
148 fun rememberNavigationSuiteScaffoldState(
149 initialValue: NavigationSuiteScaffoldValue = NavigationSuiteScaffoldValue.Visible
150 ): NavigationSuiteScaffoldState {
151 return rememberSaveable(saver = NavigationSuiteScaffoldStateImpl.Saver()) {
152 NavigationSuiteScaffoldStateImpl(initialValue = initialValue)
153 }
154 }
155
156 /**
157 * The Navigation Suite Scaffold wraps the provided content and places the adequate provided
158 * navigation component on the screen according to the current [NavigationSuiteType].
159 *
160 * The navigation component can be animated to be hidden or shown via a
161 * [NavigationSuiteScaffoldState].
162 *
163 * The scaffold also supports an optional primary action composable, such as a floating action
164 * button, which will be displayed according to the current [NavigationSuiteType].
165 *
166 * A simple usage example looks like this:
167 *
168 * @sample androidx.compose.material3.adaptive.navigationsuite.samples.NavigationSuiteScaffoldSample
169 *
170 * An usage with custom layout choices looks like this:
171 *
172 * @sample androidx.compose.material3.adaptive.navigationsuite.samples.NavigationSuiteScaffoldCustomConfigSample
173 * @param navigationItems the navigation items to be displayed, typically [NavigationSuiteItem]s
174 * @param modifier the [Modifier] to be applied to the navigation suite scaffold
175 * @param navigationSuiteType the current [NavigationSuiteType]. Defaults to
176 * [NavigationSuiteScaffoldDefaults.navigationSuiteType]
177 * @param navigationSuiteColors [NavigationSuiteColors] that will be used to determine the container
178 * (background) color of the navigation component and the preferred color for content inside the
179 * navigation component
180 * @param containerColor the color used for the background of the navigation suite scaffold,
181 * including the passed [content] composable. Use [Color.Transparent] to have no color
182 * @param contentColor the preferred color to be used for typography and iconography within the
183 * passed in [content] lambda inside the navigation suite scaffold.
184 * @param state the [NavigationSuiteScaffoldState] of this navigation suite scaffold
185 * @param navigationItemVerticalArrangement the vertical arrangement of the items inside vertical
186 * navigation components (such as the types [NavigationSuiteType.WideNavigationRailCollapsed] and
187 * [NavigationSuiteType.WideNavigationRailExpanded]). It's recommended to use [Arrangement.Top],
188 * [Arrangement.Center], or [Arrangement.Bottom]. Defaults to [Arrangement.Top]
189 * @param primaryActionContent The optional primary action content of the navigation suite scaffold,
190 * if any. Typically a [androidx.compose.material3.FloatingActionButton]. It'll be displayed
191 * inside vertical navigation components as part of their header , and above horizontal navigation
192 * components.
193 * @param primaryActionContentHorizontalAlignment The horizontal alignment of the primary action
194 * content, if present, when it's displayed along with a horizontal navigation component.
195 * @param content the content of your screen
196 */
197 @Composable
NavigationSuiteScaffoldnull198 fun NavigationSuiteScaffold(
199 navigationItems: @Composable () -> Unit,
200 modifier: Modifier = Modifier,
201 navigationSuiteType: NavigationSuiteType =
202 NavigationSuiteScaffoldDefaults.navigationSuiteType(WindowAdaptiveInfoDefault),
203 navigationSuiteColors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
204 containerColor: Color = NavigationSuiteScaffoldDefaults.containerColor,
205 contentColor: Color = NavigationSuiteScaffoldDefaults.contentColor,
206 state: NavigationSuiteScaffoldState = rememberNavigationSuiteScaffoldState(),
207 navigationItemVerticalArrangement: Arrangement.Vertical =
208 NavigationSuiteDefaults.verticalArrangement,
209 primaryActionContent: @Composable (() -> Unit) = {},
210 primaryActionContentHorizontalAlignment: Alignment.Horizontal =
211 NavigationSuiteScaffoldDefaults.primaryActionContentAlignment,
212 content: @Composable () -> Unit,
213 ) {
<lambda>null214 Surface(modifier = modifier, color = containerColor, contentColor = contentColor) {
215 NavigationSuiteScaffoldLayout(
216 navigationSuite = {
217 NavigationSuite(
218 navigationSuiteType = navigationSuiteType,
219 colors = navigationSuiteColors,
220 primaryActionContent = primaryActionContent,
221 verticalArrangement = navigationItemVerticalArrangement,
222 content = navigationItems
223 )
224 },
225 navigationSuiteType = navigationSuiteType,
226 state = state,
227 primaryActionContent = primaryActionContent,
228 primaryActionContentHorizontalAlignment = primaryActionContentHorizontalAlignment,
229 content = {
230 Box(
231 Modifier.navigationSuiteScaffoldConsumeWindowInsets(navigationSuiteType, state)
232 ) {
233 content()
234 }
235 }
236 )
237 }
238 }
239
240 /**
241 * The Navigation Suite Scaffold wraps the provided content and places the adequate provided
242 * navigation component on the screen according to the current [NavigationSuiteType].
243 *
244 * Note: It is recommended to use the [NavigationSuiteScaffold] function with the navigationItems
245 * param that accepts [NavigationSuiteItem]s instead of this one.
246 *
247 * The navigation component can be animated to be hidden or shown via a
248 * [NavigationSuiteScaffoldState].
249 *
250 * @param navigationSuiteItems the navigation items to be displayed
251 * @param modifier the [Modifier] to be applied to the navigation suite scaffold
252 * @param layoutType the current [NavigationSuiteType]. Defaults to
253 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
254 * @param navigationSuiteColors [NavigationSuiteColors] that will be used to determine the container
255 * (background) color of the navigation component and the preferred color for content inside the
256 * navigation component
257 * @param containerColor the color used for the background of the navigation suite scaffold,
258 * including the passed [content] composable. Use [Color.Transparent] to have no color
259 * @param contentColor the preferred color to be used for typography and iconography within the
260 * passed in [content] lambda inside the navigation suite scaffold.
261 * @param state the [NavigationSuiteScaffoldState] of this navigation suite scaffold
262 * @param content the content of your screen
263 */
264 @OptIn(ExperimentalMaterial3AdaptiveComponentOverrideApi::class)
265 @Composable
NavigationSuiteScaffoldnull266 fun NavigationSuiteScaffold(
267 navigationSuiteItems: NavigationSuiteScope.() -> Unit,
268 modifier: Modifier = Modifier,
269 layoutType: NavigationSuiteType =
270 NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
271 navigationSuiteColors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
272 containerColor: Color = NavigationSuiteScaffoldDefaults.containerColor,
273 contentColor: Color = NavigationSuiteScaffoldDefaults.contentColor,
274 state: NavigationSuiteScaffoldState = rememberNavigationSuiteScaffoldState(),
275 content: @Composable () -> Unit = {},
276 ) {
<lambda>null277 with(LocalNavigationSuiteScaffoldOverride.current) {
278 NavigationSuiteScaffoldOverrideScope(
279 navigationSuiteItems = navigationSuiteItems,
280 modifier = modifier,
281 layoutType = layoutType,
282 navigationSuiteColors = navigationSuiteColors,
283 containerColor = containerColor,
284 contentColor = contentColor,
285 state = state,
286 content = content
287 )
288 .NavigationSuiteScaffold()
289 }
290 }
291
292 /**
293 * This override provides the default behavior of the [NavigationSuiteScaffold] component.
294 *
295 * [NavigationSuiteScaffoldOverride] used when no override is specified.
296 */
297 @ExperimentalMaterial3AdaptiveComponentOverrideApi
298 object DefaultNavigationSuiteScaffoldOverride : NavigationSuiteScaffoldOverride {
299 @Composable
NavigationSuiteScaffoldnull300 override fun NavigationSuiteScaffoldOverrideScope.NavigationSuiteScaffold() {
301 Surface(modifier = modifier, color = containerColor, contentColor = contentColor) {
302 NavigationSuiteScaffoldLayout(
303 navigationSuite = {
304 NavigationSuite(
305 layoutType = layoutType,
306 colors = navigationSuiteColors,
307 content = navigationSuiteItems
308 )
309 },
310 state = state,
311 layoutType = layoutType,
312 content = {
313 Box(
314 Modifier.consumeWindowInsets(
315 if (
316 state.currentValue == NavigationSuiteScaffoldValue.Hidden &&
317 !state.isAnimating
318 ) {
319 NoWindowInsets
320 } else {
321 when (layoutType) {
322 NavigationSuiteType.NavigationBar ->
323 NavigationBarDefaults.windowInsets.only(
324 WindowInsetsSides.Bottom
325 )
326 NavigationSuiteType.NavigationRail ->
327 NavigationRailDefaults.windowInsets.only(
328 WindowInsetsSides.Start
329 )
330 NavigationSuiteType.NavigationDrawer ->
331 DrawerDefaults.windowInsets.only(WindowInsetsSides.Start)
332 else -> NoWindowInsets
333 }
334 }
335 )
336 ) {
337 content()
338 }
339 }
340 )
341 }
342 }
343 }
344
345 /**
346 * The Navigation Suite Scaffold wraps the provided content and places the adequate provided
347 * navigation component on the screen according to the current [NavigationSuiteType].
348 *
349 * @param navigationSuiteItems the navigation items to be displayed
350 * @param modifier the [Modifier] to be applied to the navigation suite scaffold
351 * @param layoutType the current [NavigationSuiteType]. Defaults to
352 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
353 * @param navigationSuiteColors [NavigationSuiteColors] that will be used to determine the container
354 * (background) color of the navigation component and the preferred color for content inside the
355 * navigation component
356 * @param containerColor the color used for the background of the navigation suite scaffold,
357 * including the passed [content] composable. Use [Color.Transparent] to have no color
358 * @param contentColor the preferred color to be used for typography and iconography within the
359 * passed in [content] lambda inside the navigation suite scaffold.
360 * @param content the content of your screen
361 */
362 @Deprecated(
363 message = "Deprecated in favor of NavigationSuiteScaffold with state parameter",
364 level = DeprecationLevel.HIDDEN
365 )
366 @Composable
NavigationSuiteScaffoldnull367 fun NavigationSuiteScaffold(
368 navigationSuiteItems: NavigationSuiteScope.() -> Unit,
369 modifier: Modifier = Modifier,
370 layoutType: NavigationSuiteType =
371 NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
372 navigationSuiteColors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
373 containerColor: Color = NavigationSuiteScaffoldDefaults.containerColor,
374 contentColor: Color = NavigationSuiteScaffoldDefaults.contentColor,
375 content: @Composable () -> Unit = {},
376 ) =
377 NavigationSuiteScaffold(
378 navigationSuiteItems = navigationSuiteItems,
379 modifier = modifier,
380 state = rememberNavigationSuiteScaffoldState(),
381 layoutType = layoutType,
382 navigationSuiteColors = navigationSuiteColors,
383 containerColor = containerColor,
384 contentColor = contentColor,
385 content = content
386 )
387
388 /**
389 * Layout for a [NavigationSuiteScaffold]'s content. This function wraps the [content] and places
390 * the [navigationSuite], and the [primaryActionContent], if any, according to the current
391 * [NavigationSuiteType].
392 *
393 * The usage of this function is recommended when you need some customization that is not viable via
394 * the use of [NavigationSuiteScaffold]. An usage example of using a custom modal wide rail can be
395 * found at androidx.compose.material3.demos.NavigationSuiteScaffoldCustomConfigDemo.
396 *
397 * @param navigationSuite the navigation component to be displayed, typically [NavigationSuite]
398 * @param navigationSuiteType the current [NavigationSuiteType]. Usually
399 * [NavigationSuiteScaffoldDefaults.navigationSuiteType]
400 * @param state the [NavigationSuiteScaffoldState] of this navigation suite scaffold layout
401 * @param primaryActionContent The optional primary action content of the navigation suite scaffold,
402 * if any. Typically a [androidx.compose.material3.FloatingActionButton]. It'll be displayed
403 * inside vertical navigation components as part of their header, and above horizontal navigation
404 * components.
405 * @param primaryActionContentHorizontalAlignment The horizontal alignment of the primary action
406 * content, if present, when it's displayed along with a horizontal navigation component.
407 * @param content the content of your screen
408 */
409 @Composable
NavigationSuiteScaffoldLayoutnull410 fun NavigationSuiteScaffoldLayout(
411 navigationSuite: @Composable () -> Unit,
412 navigationSuiteType: NavigationSuiteType,
413 state: NavigationSuiteScaffoldState = rememberNavigationSuiteScaffoldState(),
414 primaryActionContent: @Composable (() -> Unit) = {},
415 primaryActionContentHorizontalAlignment: Alignment.Horizontal =
416 NavigationSuiteScaffoldDefaults.primaryActionContentAlignment,
417 content: @Composable () -> Unit
418 ) {
419 val animationProgress by
420 animateFloatAsState(
421 targetValue = if (state.currentValue == NavigationSuiteScaffoldValue.Hidden) 0f else 1f,
422 animationSpec = AnimationSpec
423 )
424
<lambda>null425 Layout({
426 // Wrap the navigation suite and content composables each in a Box to not propagate the
427 // parent's (Surface) min constraints to its children (see b/312664933).
428 Box(Modifier.layoutId(NavigationSuiteLayoutIdTag)) { navigationSuite() }
429 Box(Modifier.layoutId(PrimaryActionContentLayoutIdTag)) { primaryActionContent() }
430 Box(Modifier.layoutId(ContentLayoutIdTag)) { content() }
431 }) { measurables, constraints ->
432 val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
433 // Find the navigation suite composable through it's layoutId tag
434 val navigationPlaceable =
435 measurables
<lambda>null436 .fastFirst { it.layoutId == NavigationSuiteLayoutIdTag }
437 .measure(looseConstraints)
438 val primaryActionContentPlaceable =
439 measurables
<lambda>null440 .fastFirst { it.layoutId == PrimaryActionContentLayoutIdTag }
441 .measure(looseConstraints)
442 val isNavigationBar = navigationSuiteType.isNavigationBar
443 val layoutHeight = constraints.maxHeight
444 val layoutWidth = constraints.maxWidth
445 // Find the content composable through it's layoutId tag.
446 val contentPlaceable =
447 measurables
<lambda>null448 .fastFirst { it.layoutId == ContentLayoutIdTag }
449 .measure(
450 if (isNavigationBar) {
451 constraints.copy(
452 minHeight =
453 layoutHeight -
454 (navigationPlaceable.height * animationProgress).toInt(),
455 maxHeight =
456 layoutHeight -
457 (navigationPlaceable.height * animationProgress).toInt()
458 )
459 } else {
460 constraints.copy(
461 minWidth =
462 layoutWidth -
463 (navigationPlaceable.width * animationProgress).toInt(),
464 maxWidth =
465 layoutWidth -
466 (navigationPlaceable.width * animationProgress).toInt()
467 )
468 }
469 )
470
<lambda>null471 layout(layoutWidth, layoutHeight) {
472 if (isNavigationBar) {
473 // Place content above the navigation component.
474 contentPlaceable.placeRelative(0, 0)
475 // Place the navigation component at the bottom of the screen.
476 navigationPlaceable.placeRelative(
477 0,
478 layoutHeight - (navigationPlaceable.height * animationProgress).toInt()
479 )
480 // Place the primary action content above the navigation component.
481 val positionX =
482 if (primaryActionContentHorizontalAlignment == Alignment.Start) {
483 PrimaryActionContentPadding.roundToPx()
484 } else if (
485 primaryActionContentHorizontalAlignment == Alignment.CenterHorizontally
486 ) {
487 (layoutWidth - primaryActionContentPlaceable.width) / 2
488 } else {
489 layoutWidth -
490 primaryActionContentPlaceable.width -
491 PrimaryActionContentPadding.roundToPx()
492 }
493 primaryActionContentPlaceable.placeRelative(
494 positionX,
495 layoutHeight -
496 primaryActionContentPlaceable.height -
497 PrimaryActionContentPadding.roundToPx() -
498 (navigationPlaceable.height * animationProgress).toInt()
499 )
500 } else {
501 // Place the navigation component at the start of the screen.
502 navigationPlaceable.placeRelative(
503 (0 - (navigationPlaceable.width * (1f - animationProgress))).toInt(),
504 0
505 )
506 // Place content to the side of the navigation component.
507 contentPlaceable.placeRelative(
508 (navigationPlaceable.width * animationProgress).toInt(),
509 0
510 )
511 }
512 }
513 }
514 }
515
516 /**
517 * Layout for a [NavigationSuiteScaffold]'s content. This function wraps the [content] and places
518 * the [navigationSuite] component according to the given [layoutType].
519 *
520 * Note: It is recommended to use the [NavigationSuiteScaffoldLayout] function with the
521 * navigationSuiteType param instead of this one.
522 *
523 * The usage of this function is recommended when you need some customization that is not viable via
524 * the use of [NavigationSuiteScaffold].
525 *
526 * @param navigationSuite the navigation component to be displayed, typically [NavigationSuite]
527 * @param layoutType the current [NavigationSuiteType]. Defaults to
528 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
529 * @param state the [NavigationSuiteScaffoldState] of this navigation suite scaffold layout
530 * @param content the content of your screen
531 */
532 @Composable
NavigationSuiteScaffoldLayoutnull533 fun NavigationSuiteScaffoldLayout(
534 navigationSuite: @Composable () -> Unit,
535 layoutType: NavigationSuiteType =
536 NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
537 state: NavigationSuiteScaffoldState = rememberNavigationSuiteScaffoldState(),
538 content: @Composable () -> Unit = {}
539 ) {
540 NavigationSuiteScaffoldLayout(
541 navigationSuite = navigationSuite,
542 navigationSuiteType = layoutType,
543 state = state,
544 content = content
545 )
546 }
547
548 /**
549 * Layout for a [NavigationSuiteScaffold]'s content. This function wraps the [content] and places
550 * the [navigationSuite] component according to the given [layoutType].
551 *
552 * The usage of this function is recommended when you need some customization that is not viable via
553 * the use of [NavigationSuiteScaffold]. Example usage:
554 *
555 * @param navigationSuite the navigation component to be displayed, typically [NavigationSuite]
556 * @param layoutType the current [NavigationSuiteType]. Defaults to
557 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
558 * @param content the content of your screen
559 */
560 @Deprecated(
561 message = "Deprecated in favor of NavigationSuiteScaffoldLayout with state parameter",
562 level = DeprecationLevel.HIDDEN
563 )
564 @Composable
NavigationSuiteScaffoldLayoutnull565 fun NavigationSuiteScaffoldLayout(
566 navigationSuite: @Composable () -> Unit,
567 layoutType: NavigationSuiteType =
568 NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
569 content: @Composable () -> Unit = {}
570 ) =
571 NavigationSuiteScaffoldLayout(
572 navigationSuite = navigationSuite,
573 navigationSuiteType = layoutType,
574 state = rememberNavigationSuiteScaffoldState(),
575 content = content
576 )
577
578 /**
579 * The default Material navigation component according to the current [NavigationSuiteType] to be
580 * used with the [NavigationSuiteScaffold].
581 *
582 * For specifics about each navigation component, see [ShortNavigationBar], [WideNavigationRail],
583 * [NavigationRail], and [PermanentDrawerSheet].
584 *
585 * @param navigationSuiteType the [NavigationSuiteType] of the associated [NavigationSuiteScaffold].
586 * Usually [NavigationSuiteScaffoldDefaults.navigationSuiteType]
587 * @param modifier the [Modifier] to be applied to the navigation component
588 * @param colors [NavigationSuiteColors] that will be used to determine the container (background)
589 * color of the navigation component and the preferred color for content inside the navigation
590 * component
591 * @param verticalArrangement the vertical arrangement of the items inside vertical navigation
592 * components, such as the wide navigation rail. It's recommended to use [Arrangement.Top],
593 * [Arrangement.Center], or [Arrangement.Bottom].
594 * @param primaryActionContent The optional primary action content of the navigation suite scaffold,
595 * if any. Typically a [androidx.compose.material3.FloatingActionButton]. It'll be displayed
596 * inside vertical navigation components as their header, and above horizontal navigation
597 * components.
598 * @param content the content inside the current navigation component, typically
599 * [NavigationSuiteItem]s
600 */
601 @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3ExpressiveApi::class)
602 @Composable
NavigationSuitenull603 fun NavigationSuite(
604 navigationSuiteType: NavigationSuiteType,
605 modifier: Modifier = Modifier,
606 colors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
607 verticalArrangement: Arrangement.Vertical = NavigationSuiteDefaults.verticalArrangement,
608 primaryActionContent: @Composable (() -> Unit) = {},
609 content: @Composable () -> Unit
610 ) {
<lambda>null611 val movableContent = remember(content) { movableContentOf(content) }
612 when (navigationSuiteType) {
613 NavigationSuiteType.ShortNavigationBarCompact -> {
614 ShortNavigationBar(
615 modifier = modifier,
616 containerColor = colors.shortNavigationBarContainerColor,
617 contentColor = colors.shortNavigationBarContentColor,
618 content = movableContent
619 )
620 }
621 NavigationSuiteType.ShortNavigationBarMedium -> {
622 ShortNavigationBar(
623 modifier = modifier,
624 containerColor = colors.shortNavigationBarContainerColor,
625 contentColor = colors.shortNavigationBarContentColor,
626 content = movableContent
627 )
628 }
629 NavigationSuiteType.WideNavigationRailCollapsed -> {
630 WideNavigationRail(
631 modifier = modifier,
632 header = primaryActionContent,
633 arrangement = verticalArrangement,
634 colors = colors.wideNavigationRailColors,
635 content = movableContent
636 )
637 }
638 NavigationSuiteType.WideNavigationRailExpanded -> {
639 WideNavigationRail(
640 modifier = modifier,
641 header = primaryActionContent,
642 state =
643 rememberWideNavigationRailState(
644 initialValue = WideNavigationRailValue.Expanded
645 ),
646 arrangement = verticalArrangement,
647 colors = colors.wideNavigationRailColors,
648 content = movableContent
649 )
650 }
651 // Note: This function does not support providing a NavigationBar for the
652 // NavigationSuiteType.NavigationBar type instead provides a ShortNavigationBar with a
653 // taller height so that it is visually the same.
654 // It's advised to to use NavigationSuiteType.ShortNavigationBarVerticalItems instead.
655 NavigationSuiteType.NavigationBar -> {
656 ShortNavigationBar(
657 modifier = modifier.heightIn(min = TallNavigationBarHeight),
658 containerColor = colors.navigationBarContainerColor,
659 contentColor = colors.navigationBarContentColor
<lambda>null660 ) {
661 movableContent()
662 }
663 }
664 // It's advised to to use NavigationSuiteType.WideNavigationRail instead of
665 // NavigationSuiteType.NavigationRail.
666 NavigationSuiteType.NavigationRail -> {
667 NavigationRail(
668 modifier = modifier,
<lambda>null669 header = { primaryActionContent() },
670 containerColor = colors.navigationRailContainerColor,
671 contentColor = colors.navigationRailContentColor
<lambda>null672 ) {
673 if (
674 verticalArrangement == Arrangement.Center ||
675 verticalArrangement == Arrangement.Bottom
676 ) {
677 Spacer(Modifier.weight(1f))
678 }
679 movableContent()
680 if (verticalArrangement == Arrangement.Center) {
681 Spacer(Modifier.weight(1f))
682 }
683 }
684 }
685 // It's advised to to use NavigationSuiteType.WideNavigationRail instead of
686 // NavigationSuiteType.NavigationDrawer.
687 NavigationSuiteType.NavigationDrawer -> {
688 PermanentDrawerSheet(
689 modifier = modifier,
690 drawerContainerColor = colors.navigationDrawerContainerColor,
691 drawerContentColor = colors.navigationDrawerContentColor
<lambda>null692 ) {
693 primaryActionContent()
694 if (
695 verticalArrangement == Arrangement.Center ||
696 verticalArrangement == Arrangement.Bottom
697 ) {
698 Spacer(Modifier.weight(1f))
699 }
700 movableContent()
701 if (verticalArrangement == Arrangement.Center) {
702 Spacer(Modifier.weight(1f))
703 }
704 }
705 }
706 }
707 }
708
709 /**
710 * The default Material navigation component according to the current [NavigationSuiteType] to be
711 * used with the [NavigationSuiteScaffold].
712 *
713 * Note: It is recommended to use the [NavigationSuite] function with the navigationSuiteType param
714 * and that accepts [NavigationSuiteItem]s instead of this one.
715 *
716 * For specifics about each navigation component, see [NavigationBar], [NavigationRail], and
717 * [PermanentDrawerSheet].
718 *
719 * @param modifier the [Modifier] to be applied to the navigation component
720 * @param layoutType the current [NavigationSuiteType] of the [NavigationSuiteScaffold]. Defaults to
721 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
722 * @param colors [NavigationSuiteColors] that will be used to determine the container (background)
723 * color of the navigation component and the preferred color for content inside the navigation
724 * component
725 * @param content the content inside the current navigation component, typically
726 * [NavigationSuiteScope.item]s
727 */
728 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
729 @Composable
NavigationSuitenull730 fun NavigationSuite(
731 modifier: Modifier = Modifier,
732 layoutType: NavigationSuiteType =
733 NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
734 colors: NavigationSuiteColors = NavigationSuiteDefaults.colors(),
735 content: NavigationSuiteScope.() -> Unit
736 ) {
737 val scope by rememberStateOfItems(content)
738 // Define defaultItemColors here since we can't set NavigationSuiteDefaults.itemColors() as a
739 // default for the colors param of the NavigationSuiteScope.item non-composable function.
740 val defaultItemColors = NavigationSuiteDefaults.itemColors()
741
742 when (layoutType) {
743 NavigationSuiteType.NavigationBar -> {
744 NavigationBar(
745 modifier = modifier,
746 containerColor = colors.navigationBarContainerColor,
747 contentColor = colors.navigationBarContentColor
748 ) {
749 scope.itemList.forEach {
750 NavigationBarItem(
751 modifier = it.modifier,
752 selected = it.selected,
753 onClick = it.onClick,
754 icon = { NavigationItemIcon(icon = it.icon, badge = it.badge) },
755 enabled = it.enabled,
756 label = it.label,
757 alwaysShowLabel = it.alwaysShowLabel,
758 colors =
759 it.colors?.navigationBarItemColors
760 ?: defaultItemColors.navigationBarItemColors,
761 interactionSource = it.interactionSource
762 )
763 }
764 }
765 }
766 NavigationSuiteType.NavigationRail -> {
767 NavigationRail(
768 modifier = modifier,
769 containerColor = colors.navigationRailContainerColor,
770 contentColor = colors.navigationRailContentColor
771 ) {
772 scope.itemList.forEach {
773 NavigationRailItem(
774 modifier = it.modifier,
775 selected = it.selected,
776 onClick = it.onClick,
777 icon = { NavigationItemIcon(icon = it.icon, badge = it.badge) },
778 enabled = it.enabled,
779 label = it.label,
780 alwaysShowLabel = it.alwaysShowLabel,
781 colors =
782 it.colors?.navigationRailItemColors
783 ?: defaultItemColors.navigationRailItemColors,
784 interactionSource = it.interactionSource
785 )
786 }
787 }
788 }
789 NavigationSuiteType.NavigationDrawer -> {
790 PermanentDrawerSheet(
791 modifier = modifier,
792 drawerContainerColor = colors.navigationDrawerContainerColor,
793 drawerContentColor = colors.navigationDrawerContentColor
794 ) {
795 scope.itemList.forEach {
796 NavigationDrawerItem(
797 modifier = it.modifier,
798 selected = it.selected,
799 onClick = it.onClick,
800 icon = it.icon,
801 badge = it.badge,
802 label = { it.label?.invoke() ?: Text("") },
803 colors =
804 it.colors?.navigationDrawerItemColors
805 ?: defaultItemColors.navigationDrawerItemColors,
806 interactionSource = it.interactionSource
807 )
808 }
809 }
810 }
811 NavigationSuiteType.None -> {
812 /* Do nothing. */
813 }
814 else -> {
815 NavigationSuite(
816 navigationSuiteType = layoutType,
817 modifier = modifier,
818 colors = colors,
819 ) {
820 scope.itemList.forEach {
821 NavigationSuiteItem(
822 isNavigationSuite = true,
823 navigationSuiteType = layoutType,
824 modifier = it.modifier,
825 selected = it.selected,
826 onClick = it.onClick,
827 icon = it.icon,
828 badge = it.badge,
829 enabled = it.enabled,
830 label = it.label,
831 navigationSuiteItemColors =
832 it.colors ?: NavigationSuiteDefaults.itemColors(),
833 navigationItemColors = null,
834 interactionSource = it.interactionSource
835 )
836 }
837 }
838 }
839 }
840 }
841
842 /**
843 * The default Material navigation item component according to the current [NavigationSuiteType] to
844 * be used with the [NavigationSuite] that accepts this function.
845 *
846 * For specifics about each navigation component, see [ShortNavigationBarItem],
847 * [WideNavigationRailItem], [NavigationRailItem], and [NavigationDrawerItem].
848 *
849 * @param selected whether this item is selected
850 * @param onClick called when this item is clicked
851 * @param icon icon for this item, typically an [Icon]
852 * @param label the text label for this item
853 * @param modifier the [Modifier] to be applied to this item
854 * @param navigationSuiteType the current [NavigationSuiteType] of the associated [NavigationSuite].
855 * Defaults to [NavigationSuiteScaffoldDefaults.navigationSuiteType]
856 * @param enabled controls the enabled state of this item. When `false`, this component will not
857 * respond to user input, and it will appear visually disabled and disabled to accessibility
858 * services. Note: as of now, for [NavigationDrawerItem], this is always `true`.
859 * @param badge optional badge to show on this item
860 * @param colors [NavigationItemColors] that will be used to resolve the colors used for this item
861 * in different states. If null, a default Material colors for each specific item will be used.
862 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
863 * emitting [Interaction]s for this item. You can use this to change the item's appearance or
864 * preview the item in different states. Note that if `null` is provided, interactions will still
865 * happen internally.
866 */
867 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
868 @Composable
NavigationSuiteItemnull869 fun NavigationSuiteItem(
870 selected: Boolean,
871 onClick: () -> Unit,
872 icon: @Composable () -> Unit,
873 label: @Composable (() -> Unit)?,
874 modifier: Modifier = Modifier,
875 navigationSuiteType: NavigationSuiteType =
876 NavigationSuiteScaffoldDefaults.navigationSuiteType(WindowAdaptiveInfoDefault),
877 enabled: Boolean = true,
878 badge: @Composable (() -> Unit)? = null,
879 colors: NavigationItemColors? = null,
880 interactionSource: MutableInteractionSource? = null,
881 ) {
882 NavigationSuiteItem(
883 isNavigationSuite = false,
884 navigationSuiteType = navigationSuiteType,
885 selected = selected,
886 onClick = onClick,
887 icon = icon,
888 label = label,
889 modifier = modifier,
890 enabled = enabled,
891 badge = badge,
892 navigationItemColors = colors,
893 navigationSuiteItemColors = null,
894 interactionSource = interactionSource,
895 )
896 }
897
898 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
899 @Composable
NavigationSuiteItemnull900 private fun NavigationSuiteItem(
901 isNavigationSuite: Boolean,
902 navigationSuiteType: NavigationSuiteType,
903 selected: Boolean,
904 onClick: () -> Unit,
905 icon: @Composable () -> Unit,
906 label: @Composable (() -> Unit)?,
907 modifier: Modifier,
908 enabled: Boolean,
909 badge: @Composable (() -> Unit)?,
910 navigationItemColors: NavigationItemColors?,
911 navigationSuiteItemColors: NavigationSuiteItemColors?,
912 interactionSource: MutableInteractionSource?
913 ) {
914 when (navigationSuiteType) {
915 NavigationSuiteType.ShortNavigationBarCompact,
916 NavigationSuiteType.ShortNavigationBarMedium -> {
917 val iconPosition =
918 if (navigationSuiteType == NavigationSuiteType.ShortNavigationBarCompact) {
919 NavigationItemIconPosition.Top
920 } else {
921 NavigationItemIconPosition.Start
922 }
923 ShortNavigationBarItem(
924 selected = selected,
925 onClick = onClick,
926 icon = { NavigationItemIcon(icon = icon, badge = badge) },
927 label = label,
928 modifier = modifier,
929 enabled = enabled,
930 iconPosition = iconPosition,
931 colors = navigationItemColors ?: ShortNavigationBarItemDefaults.colors(),
932 interactionSource = interactionSource
933 )
934 }
935 NavigationSuiteType.WideNavigationRailCollapsed,
936 NavigationSuiteType.WideNavigationRailExpanded -> {
937 WideNavigationRailItem(
938 railExpanded =
939 navigationSuiteType == NavigationSuiteType.WideNavigationRailExpanded,
940 selected = selected,
941 onClick = onClick,
942 icon = { NavigationItemIcon(icon = icon, badge = badge) },
943 label = label,
944 modifier = modifier,
945 enabled = enabled,
946 colors = navigationItemColors ?: WideNavigationRailItemDefaults.colors(),
947 interactionSource = interactionSource
948 )
949 }
950 // Note: This function does not support providing a NavigationBarItem for the
951 // NavigationSuiteType.NavigationBar type due to the NavigationBarItem being limited to
952 // RowScope. Instead we provide ShortNavigationBarItem with a top padding so that it is
953 // visually the same.
954 // It's advised to to use NavigationSuiteType.ShortNavigationBarVerticalItems instead.
955 NavigationSuiteType.NavigationBar -> {
956 val defaultColors =
957 navigationSuiteItemColors?.navigationBarItemColors
958 ?: NavigationBarItemDefaults.colors()
959 val actualColors =
960 if ((!isNavigationSuite && navigationItemColors == null) || isNavigationSuite) {
961 ShortNavigationBarItemDefaults.colors(
962 selectedIconColor = defaultColors.selectedIconColor,
963 selectedTextColor = defaultColors.selectedTextColor,
964 selectedIndicatorColor = defaultColors.selectedIndicatorColor,
965 unselectedIconColor = defaultColors.unselectedIconColor,
966 unselectedTextColor = defaultColors.unselectedTextColor,
967 disabledIconColor = defaultColors.disabledIconColor,
968 disabledTextColor = defaultColors.disabledTextColor,
969 )
970 } else {
971 navigationItemColors!!
972 }
973
974 ShortNavigationBarItem(
975 selected = selected,
976 onClick = onClick,
977 icon = { NavigationItemIcon(icon = icon, badge = badge) },
978 label = label,
979 modifier = modifier.padding(top = 8.dp),
980 enabled = enabled,
981 colors = actualColors,
982 interactionSource = interactionSource
983 )
984 }
985 // It's advised to to use NavigationSuiteType.WideNavigationRail instead of
986 // NavigationSuiteType.NavigationRail.
987 NavigationSuiteType.NavigationRail -> {
988 val actualColors =
989 if (isNavigationSuite) {
990 navigationSuiteItemColors?.navigationRailItemColors
991 ?: NavigationRailItemDefaults.colors()
992 } else {
993 if (navigationItemColors != null) {
994 NavigationRailItemDefaults.colors(
995 selectedIconColor = navigationItemColors.selectedIconColor,
996 selectedTextColor = navigationItemColors.selectedTextColor,
997 indicatorColor = navigationItemColors.selectedIndicatorColor,
998 unselectedIconColor = navigationItemColors.unselectedIconColor,
999 unselectedTextColor = navigationItemColors.unselectedTextColor,
1000 disabledIconColor = navigationItemColors.disabledIconColor,
1001 disabledTextColor = navigationItemColors.disabledTextColor,
1002 )
1003 } else {
1004 NavigationSuiteDefaults.itemColors().navigationRailItemColors
1005 }
1006 }
1007 NavigationRailItem(
1008 selected = selected,
1009 onClick = onClick,
1010 icon = { NavigationItemIcon(icon = icon, badge = badge) },
1011 label = label,
1012 modifier = modifier,
1013 enabled = enabled,
1014 colors = actualColors,
1015 interactionSource = interactionSource
1016 )
1017 }
1018 // It's advised to to use NavigationSuiteType.WideNavigationRail instead of
1019 // NavigationSuiteType.NavigationDrawer.
1020 NavigationSuiteType.NavigationDrawer -> {
1021 val actualColors =
1022 if (isNavigationSuite) {
1023 navigationSuiteItemColors?.navigationDrawerItemColors
1024 ?: NavigationDrawerItemDefaults.colors()
1025 } else {
1026 if (navigationItemColors != null) {
1027 NavigationDrawerItemDefaults.colors(
1028 selectedIconColor = navigationItemColors.selectedIconColor,
1029 selectedTextColor = navigationItemColors.selectedTextColor,
1030 unselectedIconColor = navigationItemColors.unselectedIconColor,
1031 unselectedTextColor = navigationItemColors.unselectedTextColor,
1032 selectedContainerColor = navigationItemColors.selectedIndicatorColor
1033 )
1034 } else {
1035 NavigationSuiteDefaults.itemColors().navigationDrawerItemColors
1036 }
1037 }
1038
1039 NavigationDrawerItem(
1040 modifier = modifier,
1041 selected = selected,
1042 onClick = onClick,
1043 icon = icon,
1044 badge = badge,
1045 label = { label?.invoke() ?: Text("") },
1046 colors = actualColors,
1047 interactionSource = interactionSource
1048 )
1049 }
1050 }
1051 }
1052
1053 /** The scope associated with the [NavigationSuiteScope]. */
1054 sealed interface NavigationSuiteScope {
1055
1056 /**
1057 * This function sets the parameters of the default Material navigation item to be used with the
1058 * Navigation Suite Scaffold. The item is called in [NavigationSuite], according to the current
1059 * [NavigationSuiteType].
1060 *
1061 * For specifics about each item component, see [NavigationBarItem], [NavigationRailItem], and
1062 * [NavigationDrawerItem].
1063 *
1064 * @param selected whether this item is selected
1065 * @param onClick called when this item is clicked
1066 * @param icon icon for this item, typically an [Icon]
1067 * @param modifier the [Modifier] to be applied to this item
1068 * @param enabled controls the enabled state of this item. When `false`, this component will not
1069 * respond to user input, and it will appear visually disabled and disabled to accessibility
1070 * services. Note: as of now, for [NavigationDrawerItem], this is always `true`.
1071 * @param label the text label for this item
1072 * @param alwaysShowLabel whether to always show the label for this item. If `false`, the label
1073 * will only be shown when this item is selected. Note: for [NavigationDrawerItem] this is
1074 * always `true`
1075 * @param badge optional badge to show on this item
1076 * @param colors [NavigationSuiteItemColors] that will be used to resolve the colors used for
1077 * this item in different states. If null, [NavigationSuiteDefaults.itemColors] will be used.
1078 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
1079 * emitting [Interaction]s for this item. You can use this to change the item's appearance or
1080 * preview the item in different states. Note that if `null` is provided, interactions will
1081 * still happen internally.
1082 */
itemnull1083 fun item(
1084 selected: Boolean,
1085 onClick: () -> Unit,
1086 icon: @Composable () -> Unit,
1087 modifier: Modifier = Modifier,
1088 enabled: Boolean = true,
1089 label: @Composable (() -> Unit)? = null,
1090 alwaysShowLabel: Boolean = true,
1091 badge: (@Composable () -> Unit)? = null,
1092 colors: NavigationSuiteItemColors? = null,
1093 interactionSource: MutableInteractionSource? = null
1094 )
1095 }
1096
1097 /**
1098 * Class that describes the different navigation suite types of the [NavigationSuiteScaffold].
1099 *
1100 * The [NavigationSuiteType] informs the [NavigationSuite] of what navigation component to expect.
1101 */
1102 @JvmInline
1103 value class NavigationSuiteType private constructor(private val description: String) {
1104 override fun toString(): String {
1105 return description
1106 }
1107
1108 companion object {
1109 /**
1110 * A navigation suite type that instructs the [NavigationSuite] to expect a
1111 * [ShortNavigationBar] with vertical [ShortNavigationBarItem]s that will be displayed at
1112 * the bottom of the screen.
1113 *
1114 * @see [ShortNavigationBar]
1115 */
1116 val ShortNavigationBarCompact =
1117 NavigationSuiteType(description = "ShortNavigationBarCompact")
1118
1119 /**
1120 * A navigation suite type that instructs the [NavigationSuite] to expect a
1121 * [ShortNavigationBar] with horizontal [ShortNavigationBarItem]s that will be displayed at
1122 * the bottom of the screen.
1123 *
1124 * @see [ShortNavigationBar]
1125 */
1126 val ShortNavigationBarMedium = NavigationSuiteType(description = "ShortNavigationBarMedium")
1127
1128 /**
1129 * A navigation suite type that instructs the [NavigationSuite] to expect a collapsed
1130 * [WideNavigationRail] that will be displayed at the start of the screen.
1131 *
1132 * @see [WideNavigationRail]
1133 */
1134 val WideNavigationRailCollapsed =
1135 NavigationSuiteType(description = "WideNavigationRailCollapsed")
1136
1137 /**
1138 * A navigation suite type that instructs the [NavigationSuite] to expect an expanded
1139 * [WideNavigationRail] that will be displayed at the start of the screen.
1140 *
1141 * @see [WideNavigationRail]
1142 */
1143 val WideNavigationRailExpanded =
1144 NavigationSuiteType(description = "WideNavigationRailExpanded")
1145
1146 /**
1147 * A navigation suite type that instructs the [NavigationSuite] to expect a [NavigationBar]
1148 * that will be displayed at the bottom of the screen.
1149 *
1150 * Note: It's recommended to use [ShortNavigationBarCompact] instead of this layout type.
1151 *
1152 * @see [NavigationBar]
1153 */
1154 val NavigationBar = NavigationSuiteType(description = "NavigationBar")
1155
1156 /**
1157 * A navigation suite type that instructs the [NavigationSuite] to expect a [NavigationRail]
1158 * that will be displayed at the start of the screen.
1159 *
1160 * Note: It's recommended to use [WideNavigationRailCollapsed] instead of this layout type.
1161 *
1162 * @see [NavigationRail]
1163 */
1164 val NavigationRail = NavigationSuiteType(description = "NavigationRail")
1165
1166 /**
1167 * A navigation suite type that instructs the [NavigationSuite] to expect a
1168 * [PermanentDrawerSheet] that will be displayed at the start of the screen.
1169 *
1170 * Note: It's recommended to use [WideNavigationRailExpanded] instead of this layout type.
1171 *
1172 * @see [PermanentDrawerSheet]
1173 */
1174 val NavigationDrawer = NavigationSuiteType(description = "NavigationDrawer")
1175
1176 /**
1177 * A navigation suite type that instructs the [NavigationSuite] to not display any
1178 * navigation components on the screen.
1179 *
1180 * Note: It's recommended to use [NavigationSuiteScaffoldState] instead of this layout type
1181 * and set the visibility of the navigation component to hidden.
1182 */
1183 val None = NavigationSuiteType(description = "None")
1184 }
1185 }
1186
1187 /** Contains the default values used by the [NavigationSuiteScaffold]. */
1188 object NavigationSuiteScaffoldDefaults {
1189 /**
1190 * Returns the recommended [NavigationSuiteType] according to the provided [WindowAdaptiveInfo],
1191 * following the Material specifications. Usually used with the [NavigationSuiteScaffold] and
1192 * related APIs.
1193 *
1194 * @param adaptiveInfo the provided [WindowAdaptiveInfo]
1195 * @see NavigationSuiteScaffold
1196 */
1197 @Suppress("DEPRECATION") // WindowWidthSizeClass deprecated
navigationSuiteTypenull1198 fun navigationSuiteType(adaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
1199 return with(adaptiveInfo) {
1200 if (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT) {
1201 NavigationSuiteType.ShortNavigationBarCompact
1202 } else if (
1203 windowPosture.isTabletop ||
1204 windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT &&
1205 (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.MEDIUM ||
1206 windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED)
1207 ) {
1208 NavigationSuiteType.ShortNavigationBarMedium
1209 } else {
1210 NavigationSuiteType.WideNavigationRailCollapsed
1211 }
1212 }
1213 }
1214
1215 /**
1216 * Returns the standard [NavigationSuiteType] according to the provided [WindowAdaptiveInfo].
1217 * Usually used with the [NavigationSuiteScaffold] and related APIs.
1218 *
1219 * Note: It's recommended to use [navigationSuiteType] instead of this function, as that one
1220 * offers extended and preferred types.
1221 *
1222 * @param adaptiveInfo the provided [WindowAdaptiveInfo]
1223 * @see NavigationSuiteScaffold
1224 * @see navigationSuiteType
1225 */
1226 @Suppress("DEPRECATION") // WindowWidthSizeClass deprecated
calculateFromAdaptiveInfonull1227 fun calculateFromAdaptiveInfo(adaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
1228 return with(adaptiveInfo) {
1229 if (
1230 windowPosture.isTabletop ||
1231 windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT ||
1232 windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
1233 ) {
1234 NavigationSuiteType.NavigationBar
1235 } else {
1236 NavigationSuiteType.NavigationRail
1237 }
1238 }
1239 }
1240
1241 /** Default container color for a navigation suite scaffold. */
1242 val containerColor: Color
1243 @Composable get() = MaterialTheme.colorScheme.background
1244
1245 /** Default content color for a navigation suite scaffold. */
1246 val contentColor: Color
1247 @Composable get() = MaterialTheme.colorScheme.onBackground
1248
1249 /** Default primary action content alignment for a navigation suite scaffold. */
1250 val primaryActionContentAlignment = Alignment.End
1251 }
1252
1253 /** Contains the default values used by the [NavigationSuite]. */
1254 object NavigationSuiteDefaults {
1255 /** Default items vertical arrangement for a navigation suite. */
1256 val verticalArrangement = Arrangement.Top
1257
1258 /**
1259 * Creates a [NavigationSuiteColors] with the provided colors for the container color, according
1260 * to the Material specification.
1261 *
1262 * Use [Color.Transparent] for the navigation*ContainerColor to have no color. The
1263 * navigation*ContentColor will default to either the matching content color for
1264 * navigation*ContainerColor, or to the current [LocalContentColor] if navigation*ContainerColor
1265 * is not a color from the theme.
1266 *
1267 * @param shortNavigationBarContainerColor the container color for the [ShortNavigationBar]
1268 * @param shortNavigationBarContentColor the content color for the [ShortNavigationBar]
1269 * @param wideNavigationRailColors the [WideNavigationRailColors] for the [WideNavigationRail]
1270 * @param navigationBarContainerColor the default container color for the [NavigationBar]
1271 * @param navigationBarContentColor the default content color for the [NavigationBar]
1272 * @param navigationRailContainerColor the default container color for the [NavigationRail]
1273 * @param navigationRailContentColor the default content color for the [NavigationRail]
1274 * @param navigationDrawerContainerColor the default container color for the
1275 * [PermanentDrawerSheet]
1276 * @param navigationDrawerContentColor the default content color for the [PermanentDrawerSheet]
1277 */
1278 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
1279 @Composable
colorsnull1280 fun colors(
1281 shortNavigationBarContentColor: Color = ShortNavigationBarDefaults.contentColor,
1282 shortNavigationBarContainerColor: Color = ShortNavigationBarDefaults.containerColor,
1283 wideNavigationRailColors: WideNavigationRailColors = WideNavigationRailDefaults.colors(),
1284 navigationBarContainerColor: Color = NavigationBarDefaults.containerColor,
1285 navigationBarContentColor: Color = contentColorFor(navigationBarContainerColor),
1286 navigationRailContainerColor: Color = NavigationRailDefaults.ContainerColor,
1287 navigationRailContentColor: Color = contentColorFor(navigationRailContainerColor),
1288 navigationDrawerContainerColor: Color =
1289 @Suppress("DEPRECATION") DrawerDefaults.containerColor,
1290 navigationDrawerContentColor: Color = contentColorFor(navigationDrawerContainerColor),
1291 ): NavigationSuiteColors =
1292 NavigationSuiteColors(
1293 navigationDrawerContentColor = navigationDrawerContentColor,
1294 shortNavigationBarContentColor = shortNavigationBarContentColor,
1295 shortNavigationBarContainerColor = shortNavigationBarContainerColor,
1296 wideNavigationRailColors = wideNavigationRailColors,
1297 navigationBarContainerColor = navigationBarContainerColor,
1298 navigationBarContentColor = navigationBarContentColor,
1299 navigationRailContainerColor = navigationRailContainerColor,
1300 navigationRailContentColor = navigationRailContentColor,
1301 navigationDrawerContainerColor = navigationDrawerContainerColor
1302 )
1303
1304 /**
1305 * Creates a [NavigationSuiteColors] with the provided colors for the container color, according
1306 * to the Material specification.
1307 *
1308 * Use [Color.Transparent] for the navigation*ContainerColor to have no color. The
1309 * navigation*ContentColor will default to either the matching content color for
1310 * navigation*ContainerColor, or to the current [LocalContentColor] if navigation*ContainerColor
1311 * is not a color from the theme.
1312 *
1313 * @param navigationBarContainerColor the default container color for the [NavigationBar]
1314 * @param navigationBarContentColor the default content color for the [NavigationBar]
1315 * @param navigationRailContainerColor the default container color for the [NavigationRail]
1316 * @param navigationRailContentColor the default content color for the [NavigationRail]
1317 * @param navigationDrawerContainerColor the default container color for the
1318 * [PermanentDrawerSheet]
1319 * @param navigationDrawerContentColor the default content color for the [PermanentDrawerSheet]
1320 */
1321 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
1322 @Deprecated(
1323 message =
1324 "Deprecated in favor of colors with shortNavigationBar*Color and " +
1325 "wideNavigationRailColors parameters",
1326 level = DeprecationLevel.HIDDEN
1327 )
1328 @Composable
1329 fun colors(
1330 navigationBarContainerColor: Color = NavigationBarDefaults.containerColor,
1331 navigationBarContentColor: Color = contentColorFor(navigationBarContainerColor),
1332 navigationRailContainerColor: Color = NavigationRailDefaults.ContainerColor,
1333 navigationRailContentColor: Color = contentColorFor(navigationRailContainerColor),
1334 navigationDrawerContainerColor: Color =
1335 @Suppress("DEPRECATION") DrawerDefaults.containerColor,
1336 navigationDrawerContentColor: Color = contentColorFor(navigationDrawerContainerColor),
1337 ): NavigationSuiteColors =
1338 NavigationSuiteColors(
1339 shortNavigationBarContainerColor = ShortNavigationBarDefaults.containerColor,
1340 shortNavigationBarContentColor = ShortNavigationBarDefaults.contentColor,
1341 wideNavigationRailColors = WideNavigationRailDefaults.colors(),
1342 navigationBarContainerColor = navigationBarContainerColor,
1343 navigationBarContentColor = navigationBarContentColor,
1344 navigationRailContainerColor = navigationRailContainerColor,
1345 navigationRailContentColor = navigationRailContentColor,
1346 navigationDrawerContainerColor = navigationDrawerContainerColor,
1347 navigationDrawerContentColor = navigationDrawerContentColor
1348 )
1349
1350 /**
1351 * Creates a [NavigationSuiteItemColors] with the provided colors for a
1352 * [NavigationSuiteScope.item].
1353 *
1354 * For specifics about each navigation item colors see [NavigationBarItemColors],
1355 * [NavigationRailItemColors], and [NavigationDrawerItemColors].
1356 *
1357 * @param navigationBarItemColors the [NavigationBarItemColors] associated with the
1358 * [NavigationBarItem] of the [NavigationSuiteScope.item]
1359 * @param navigationRailItemColors the [NavigationRailItemColors] associated with the
1360 * [NavigationRailItem] of the [NavigationSuiteScope.item]
1361 * @param navigationDrawerItemColors the [NavigationDrawerItemColors] associated with the
1362 * [NavigationDrawerItem] of the [NavigationSuiteScope.item]
1363 */
1364 @Composable
1365 fun itemColors(
1366 navigationBarItemColors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
1367 navigationRailItemColors: NavigationRailItemColors = NavigationRailItemDefaults.colors(),
1368 navigationDrawerItemColors: NavigationDrawerItemColors =
1369 NavigationDrawerItemDefaults.colors()
1370 ): NavigationSuiteItemColors =
1371 NavigationSuiteItemColors(
1372 navigationBarItemColors = navigationBarItemColors,
1373 navigationRailItemColors = navigationRailItemColors,
1374 navigationDrawerItemColors = navigationDrawerItemColors
1375 )
1376 }
1377
1378 /**
1379 * Represents the colors of a [NavigationSuite].
1380 *
1381 * For specifics about each navigation component colors see [NavigationBarDefaults],
1382 * [NavigationRailDefaults], and [DrawerDefaults].
1383 *
1384 * @param shortNavigationBarContainerColor the container color for the [ShortNavigationBar] of the
1385 * [NavigationSuite]
1386 * @param shortNavigationBarContentColor the content color for the [ShortNavigationBar] of the
1387 * [NavigationSuite]
1388 * @param wideNavigationRailColors the [WideNavigationRailColors] for the [WideNavigationRail] of
1389 * the [NavigationSuite]
1390 * @param navigationBarContainerColor the container color for the [NavigationBar] of the
1391 * [NavigationSuite]
1392 * @param navigationBarContentColor the content color for the [NavigationBar] of the
1393 * [NavigationSuite]
1394 * @param navigationRailContainerColor the container color for the [NavigationRail] of the
1395 * [NavigationSuite]
1396 * @param navigationRailContentColor the content color for the [NavigationRail] of the
1397 * [NavigationSuite]
1398 * @param navigationDrawerContainerColor the container color for the [PermanentDrawerSheet] of the
1399 * [NavigationSuite]
1400 * @param navigationDrawerContentColor the content color for the [PermanentDrawerSheet] of the
1401 * [NavigationSuite]
1402 */
1403 class NavigationSuiteColors
1404 internal constructor(
1405 val shortNavigationBarContainerColor: Color,
1406 val shortNavigationBarContentColor: Color,
1407 val wideNavigationRailColors: WideNavigationRailColors,
1408 val navigationBarContainerColor: Color,
1409 val navigationBarContentColor: Color,
1410 val navigationRailContainerColor: Color,
1411 val navigationRailContentColor: Color,
1412 val navigationDrawerContainerColor: Color,
1413 val navigationDrawerContentColor: Color
1414 )
1415
1416 /**
1417 * Represents the colors of a [NavigationSuiteScope.item].
1418 *
1419 * For specifics about each navigation item colors see [NavigationBarItemColors],
1420 * [NavigationRailItemColors], and [NavigationDrawerItemColors].
1421 *
1422 * @param navigationBarItemColors the [NavigationBarItemColors] associated with the
1423 * [NavigationBarItem] of the [NavigationSuiteScope.item]
1424 * @param navigationRailItemColors the [NavigationRailItemColors] associated with the
1425 * [NavigationRailItem] of the [NavigationSuiteScope.item]
1426 * @param navigationDrawerItemColors the [NavigationDrawerItemColors] associated with the
1427 * [NavigationDrawerItem] of the [NavigationSuiteScope.item]
1428 */
1429 class NavigationSuiteItemColors(
1430 val navigationBarItemColors: NavigationBarItemColors,
1431 val navigationRailItemColors: NavigationRailItemColors,
1432 val navigationDrawerItemColors: NavigationDrawerItemColors,
1433 )
1434
1435 internal val WindowAdaptiveInfoDefault
1436 @Composable get() = currentWindowAdaptiveInfo()
1437
1438 internal val NavigationSuiteScaffoldValue.isVisible
1439 get() = this == NavigationSuiteScaffoldValue.Visible
1440
1441 internal class NavigationSuiteScaffoldStateImpl(var initialValue: NavigationSuiteScaffoldValue) :
1442 NavigationSuiteScaffoldState {
1443 private val internalValue: Float = if (initialValue.isVisible) Visible else Hidden
1444 private val internalState = Animatable(internalValue, Float.VectorConverter)
1445 private val _currentVal = derivedStateOf {
1446 if (internalState.value == Visible) {
1447 NavigationSuiteScaffoldValue.Visible
1448 } else {
1449 NavigationSuiteScaffoldValue.Hidden
1450 }
1451 }
1452
1453 override val isAnimating: Boolean
1454 get() = internalState.isRunning
1455
1456 override val targetValue: NavigationSuiteScaffoldValue
1457 get() =
1458 if (internalState.targetValue == Visible) {
1459 NavigationSuiteScaffoldValue.Visible
1460 } else {
1461 NavigationSuiteScaffoldValue.Hidden
1462 }
1463
1464 override val currentValue: NavigationSuiteScaffoldValue
1465 get() = _currentVal.value
1466
1467 override suspend fun hide() {
1468 internalState.animateTo(targetValue = Hidden, animationSpec = AnimationSpec)
1469 }
1470
1471 override suspend fun show() {
1472 internalState.animateTo(targetValue = Visible, animationSpec = AnimationSpec)
1473 }
1474
1475 override suspend fun toggle() {
1476 internalState.animateTo(
1477 targetValue = if (targetValue.isVisible) Hidden else Visible,
1478 animationSpec = AnimationSpec
1479 )
1480 }
1481
1482 override suspend fun snapTo(targetValue: NavigationSuiteScaffoldValue) {
1483 val target = if (targetValue.isVisible) Visible else Hidden
1484 internalState.snapTo(target)
1485 }
1486
1487 companion object {
1488 private const val Hidden = 0f
1489 private const val Visible = 1f
1490
1491 /** The default [Saver] implementation for [NavigationSuiteScaffoldState]. */
1492 fun Saver() =
1493 Saver<NavigationSuiteScaffoldState, NavigationSuiteScaffoldValue>(
1494 save = { it.targetValue },
1495 restore = { NavigationSuiteScaffoldStateImpl(it) }
1496 )
1497 }
1498 }
1499
1500 @Composable
1501 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
Modifiernull1502 private fun Modifier.navigationSuiteScaffoldConsumeWindowInsets(
1503 navigationSuiteType: NavigationSuiteType,
1504 state: NavigationSuiteScaffoldState
1505 ): Modifier =
1506 consumeWindowInsets(
1507 if (state.currentValue == NavigationSuiteScaffoldValue.Hidden && !state.isAnimating) {
1508 NoWindowInsets
1509 } else {
1510 when (navigationSuiteType) {
1511 NavigationSuiteType.ShortNavigationBarCompact,
1512 NavigationSuiteType.ShortNavigationBarMedium ->
1513 ShortNavigationBarDefaults.windowInsets.only(WindowInsetsSides.Bottom)
1514 NavigationSuiteType.WideNavigationRailCollapsed,
1515 NavigationSuiteType.WideNavigationRailExpanded, ->
1516 WideNavigationRailDefaults.windowInsets.only(WindowInsetsSides.Start)
1517 NavigationSuiteType.NavigationBar ->
1518 NavigationBarDefaults.windowInsets.only(WindowInsetsSides.Bottom)
1519 NavigationSuiteType.NavigationRail ->
1520 NavigationRailDefaults.windowInsets.only(WindowInsetsSides.Start)
1521 NavigationSuiteType.NavigationDrawer ->
1522 DrawerDefaults.windowInsets.only(WindowInsetsSides.Start)
1523 else -> NoWindowInsets
1524 }
1525 }
1526 )
1527
1528 private val NavigationSuiteType.isNavigationBar
1529 get() =
1530 this == NavigationSuiteType.ShortNavigationBarCompact ||
1531 this == NavigationSuiteType.ShortNavigationBarMedium ||
1532 this == NavigationSuiteType.NavigationBar
1533
1534 private interface NavigationSuiteItemProvider {
1535 val itemsCount: Int
1536 val itemList: MutableVector<NavigationSuiteItem>
1537 }
1538
1539 private class NavigationSuiteItem(
1540 val selected: Boolean,
1541 val onClick: () -> Unit,
1542 val icon: @Composable () -> Unit,
1543 val modifier: Modifier,
1544 val enabled: Boolean,
1545 val label: @Composable (() -> Unit)?,
1546 val alwaysShowLabel: Boolean,
1547 val badge: (@Composable () -> Unit)?,
1548 val colors: NavigationSuiteItemColors?,
1549 val interactionSource: MutableInteractionSource?
1550 )
1551
1552 private class NavigationSuiteScopeImpl : NavigationSuiteScope, NavigationSuiteItemProvider {
1553
1554 override fun item(
1555 selected: Boolean,
1556 onClick: () -> Unit,
1557 icon: @Composable () -> Unit,
1558 modifier: Modifier,
1559 enabled: Boolean,
1560 label: @Composable (() -> Unit)?,
1561 alwaysShowLabel: Boolean,
1562 badge: (@Composable () -> Unit)?,
1563 colors: NavigationSuiteItemColors?,
1564 interactionSource: MutableInteractionSource?
1565 ) {
1566 itemList.add(
1567 NavigationSuiteItem(
1568 selected = selected,
1569 onClick = onClick,
1570 icon = icon,
1571 modifier = modifier,
1572 enabled = enabled,
1573 label = label,
1574 alwaysShowLabel = alwaysShowLabel,
1575 badge = badge,
1576 colors = colors,
1577 interactionSource = interactionSource
1578 )
1579 )
1580 }
1581
1582 override val itemList: MutableVector<NavigationSuiteItem> = mutableVectorOf()
1583
1584 override val itemsCount: Int
1585 get() = itemList.size
1586 }
1587
1588 @Composable
rememberStateOfItemsnull1589 private fun rememberStateOfItems(
1590 content: NavigationSuiteScope.() -> Unit
1591 ): State<NavigationSuiteItemProvider> {
1592 val latestContent = rememberUpdatedState(content)
1593 return remember { derivedStateOf { NavigationSuiteScopeImpl().apply(latestContent.value) } }
1594 }
1595
1596 @Composable
NavigationItemIconnull1597 private fun NavigationItemIcon(
1598 icon: @Composable () -> Unit,
1599 badge: (@Composable () -> Unit)? = null,
1600 ) {
1601 if (badge != null) {
1602 BadgedBox(badge = { badge.invoke() }) { icon() }
1603 } else {
1604 icon()
1605 }
1606 }
1607
1608 private const val SpringDefaultSpatialDamping = 0.9f
1609 private const val SpringDefaultSpatialStiffness = 700.0f
1610 private const val NavigationSuiteLayoutIdTag = "navigationSuite"
1611 private const val PrimaryActionContentLayoutIdTag = "primaryActionContent"
1612 private const val ContentLayoutIdTag = "content"
1613
1614 private val TallNavigationBarHeight = 80.dp
1615 private val PrimaryActionContentPadding = 16.dp
1616 private val NoWindowInsets = WindowInsets(0, 0, 0, 0)
1617 private val AnimationSpec: SpringSpec<Float> =
1618 spring(dampingRatio = SpringDefaultSpatialDamping, stiffness = SpringDefaultSpatialStiffness)
1619
1620 /**
1621 * Interface that allows libraries to override the behavior of the [NavigationSuiteScaffold]
1622 * component.
1623 *
1624 * To override this component, implement the member function of this interface, then provide the
1625 * implementation to [LocalNavigationSuiteScaffoldOverride] in the Compose hierarchy.
1626 */
1627 @ExperimentalMaterial3AdaptiveComponentOverrideApi
1628 interface NavigationSuiteScaffoldOverride {
1629 /** Behavior function that is called by the [NavigationSuiteScaffold] component. */
NavigationSuiteScaffoldnull1630 @Composable fun NavigationSuiteScaffoldOverrideScope.NavigationSuiteScaffold()
1631 }
1632
1633 /**
1634 * Parameters available to [NavigationSuiteScaffold].
1635 *
1636 * @param navigationSuiteItems the navigation items to be displayed
1637 * @param modifier the [Modifier] to be applied to the navigation suite scaffold
1638 * @param layoutType the current [NavigationSuiteType]. Defaults to
1639 * [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
1640 * @param navigationSuiteColors [NavigationSuiteColors] that will be used to determine the container
1641 * (background) color of the navigation component and the preferred color for content inside the
1642 * navigation component
1643 * @param containerColor the color used for the background of the navigation suite scaffold,
1644 * including the passed [content] composable. Use [Color.Transparent] to have no color
1645 * @param contentColor the preferred color to be used for typography and iconography within the
1646 * passed in [content] lambda inside the navigation suite scaffold.
1647 * @param state the [NavigationSuiteScaffoldState] of this navigation suite scaffold
1648 * @param content the content of your screen
1649 */
1650 @ExperimentalMaterial3AdaptiveComponentOverrideApi
1651 class NavigationSuiteScaffoldOverrideScope
1652 internal constructor(
1653 val navigationSuiteItems: NavigationSuiteScope.() -> Unit,
1654 val modifier: Modifier = Modifier,
1655 val layoutType: NavigationSuiteType,
1656 val navigationSuiteColors: NavigationSuiteColors,
1657 val containerColor: Color,
1658 val contentColor: Color,
1659 val state: NavigationSuiteScaffoldState,
1660 val content: @Composable () -> Unit = {},
1661 )
1662
1663 /** CompositionLocal containing the currently-selected [NavigationSuiteScaffoldOverride]. */
1664 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
1665 @get:ExperimentalMaterial3AdaptiveComponentOverrideApi
1666 @ExperimentalMaterial3AdaptiveComponentOverrideApi
1667 val LocalNavigationSuiteScaffoldOverride:
1668 ProvidableCompositionLocal<NavigationSuiteScaffoldOverride> =
<lambda>null1669 compositionLocalOf {
1670 DefaultNavigationSuiteScaffoldOverride
1671 }
1672