Indicate the associated pane should be levitated when it's the current destination.
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Composable
fun ListDetailPaneScaffoldSampleWithExtraPaneLevitatedAsDialog() {
val coroutineScope = rememberCoroutineScope()
val scaffoldNavigator = levitateAsDialogSample<NavItemData>()
val items = listOf("Item 1", "Item 2", "Item 3")
val extraItems = listOf("Extra 1", "Extra 2", "Extra 3")
val selectedItem = scaffoldNavigator.currentDestination?.contentKey
ListDetailPaneScaffold(
directive = scaffoldNavigator.scaffoldDirective,
scaffoldState = scaffoldNavigator.scaffoldState,
listPane = {
AnimatedPane(modifier = Modifier.preferredWidth(200.dp)) {
ListPaneContent(
items = items,
selectedItem = selectedItem,
scaffoldNavigator = scaffoldNavigator,
coroutineScope = coroutineScope,
)
}
},
detailPane = {
AnimatedPane {
DetailPaneContent(
items = items,
selectedItem = selectedItem,
scaffoldNavigator = scaffoldNavigator,
hasExtraPane = true,
coroutineScope = coroutineScope,
)
}
},
extraPane = {
AnimatedPane {
ExtraPaneContent(
extraItems = extraItems,
selectedItem = selectedItem,
scaffoldNavigator = scaffoldNavigator,
coroutineScope = coroutineScope,
)
}
},
paneExpansionState =
rememberPaneExpansionState(
keyProvider = scaffoldNavigator.scaffoldValue,
anchors = PaneExpansionAnchors,
initialAnchoredIndex = 1,
),
paneExpansionDragHandle = { state -> PaneExpansionDragHandleSample(state) },
)
}
/**
* This sample shows how to create a [SupportingPaneScaffold] that shows the extra pane as a bottom
* sheet when it's a single-pane layout and the extra pane is the current destination.
*
* @see levitateAsDialogSample for more usage samples of [AdaptStrategy.Levitate].
*/
@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3Api::class)
@Preview
@Composable
fun SupportingPaneScaffoldSampleWithExtraPaneLevitatedAsBottomSheet() {
val coroutineScope = rememberCoroutineScope()
val scaffoldNavigator = levitateAsBottomSheetSample<NavItemData>()
val extraItems = listOf("Extra content")
val selectedItem = NavItemData(index = 0, showExtra = true)
SupportingPaneScaffold(
directive = scaffoldNavigator.scaffoldDirective,
scaffoldState = scaffoldNavigator.scaffoldState,
mainPane = {
AnimatedPane {
MainPaneContent(
scaffoldNavigator = scaffoldNavigator,
hasExtraPane = true,
coroutineScope = coroutineScope,
)
}
},
supportingPane = {
AnimatedPane(modifier = Modifier.preferredWidth(200.dp)) { SupportingPaneContent() }
},
extraPane = {
AnimatedPane(
modifier =
Modifier.preferredWidth(1f)
.preferredHeight(0.5f)
.background(MaterialTheme.colorScheme.surface),
dragToResizeHandle = { BottomSheetDefaults.DragHandle() },
) {
ExtraPaneContent(
extraItems = extraItems,
selectedItem = selectedItem,
scaffoldNavigator = scaffoldNavigator,
coroutineScope = coroutineScope,
)
}
},
paneExpansionState =
rememberPaneExpansionState(
keyProvider = scaffoldNavigator.scaffoldValue,
anchors = PaneExpansionAnchors,
),
paneExpansionDragHandle = { state -> PaneExpansionDragHandleSample(state) },
)
}
levitateAsBottomSheetSample
/**
* This sample shows how to create a [ThreePaneScaffoldNavigator] that will show the extra pane as a
* bottom sheet in a single pane layout when the extra pane is the current destination.
*
* The key parts of this sample are:
* 1. [rememberSupportingPaneScaffoldNavigator] with a custom
* [androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies] that provides
* [AdaptStrategy.Levitate] with [Alignment.BottomCenter] for the extra pane.
* 2. The use of [androidx.compose.material3.adaptive.layout.AdaptStrategy.Levitate] with a
* remembered [DragToResizeState] with [DockedEdge.Bottom] so that the levitated extra pane can
* be resized by dragging and docked to the bottom of the layout.
*/
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Composable
fun <T> levitateAsBottomSheetSample(): ThreePaneScaffoldNavigator<T> {
val scaffoldDirective = calculatePaneScaffoldDirective(currentWindowAdaptiveInfoV2())
val dragToResizeState = rememberDragToResizeState(dockedEdge = DockedEdge.Bottom)
var navigator: ThreePaneScaffoldNavigator<T>? = null
navigator =
rememberSupportingPaneScaffoldNavigator<T>(
scaffoldDirective = scaffoldDirective,
adaptStrategies =
SupportingPaneScaffoldDefaults.adaptStrategies(
extraPaneAdaptStrategy =
AdaptStrategy.Levitate(
alignment = Alignment.BottomCenter,
dragToResizeState = dragToResizeState,
)
.onlyIfSinglePane(scaffoldDirective)
),
)
return navigator
}
levitateAsDialogSample
/**
* This sample shows how to create a [ThreePaneScaffoldNavigator] that will show the extra pane as a
* modal dialog when the extra pane is the current destination. The dialog will be centered in the
* scaffold, with a scrim that clicking on it will dismiss the dialog.
*/
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Composable
fun <T> levitateAsDialogSample(): ThreePaneScaffoldNavigator<T> {
val coroutineScope = rememberCoroutineScope()
val scaffoldDirective = calculatePaneScaffoldDirective(currentWindowAdaptiveInfoV2())
var navigator: ThreePaneScaffoldNavigator<T>? = null
val onClick: () -> Unit = { coroutineScope.launch { navigator?.navigateBack() } }
navigator =
rememberListDetailPaneScaffoldNavigator<T>(
scaffoldDirective = scaffoldDirective,
adaptStrategies =
SupportingPaneScaffoldDefaults.adaptStrategies(
extraPaneAdaptStrategy =
AdaptStrategy.Levitate(
alignment = Alignment.Center,
scrim = {
LevitatedPaneScrim(
Modifier.semantics {
contentDescription = "Scrim"
this.onClick("Dismiss the extra pane") {
onClick()
true
}
},
onClick = onClick,
)
},
)
.onlyIfSinglePane(scaffoldDirective)
),
)
return navigator
}