The supporting pane canonical layout focuses user attention on your app's main
content while also displaying relevant supporting content. For example, the main
content pane might show information about a recent movie while the supporting
pane displays a list of other movies that have a similar theme or the same
director or starring actors. For more information about the supporting pane
canonical layout, see the
Material 3 supporting pane guidelines
.
Implement a supporting pane
SupportingPaneScaffold
consists of up to three
panes: a main pane, a supporting pane, and an optional extra pane. The scaffold
handles all calculations for allocating window space to the three panes. On
large screens, the scaffold displays the main pane with the supporting pane on
the side. On small screens, the scaffold displays either the main or supporting
pane full screen.
Add dependencies
SupportingPaneScaffold
is part of the
Material 3 adaptive layout library
.
Add the following three, related dependencies to the
build.gradle
file of your
app or module:
Kotlin
implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
Groovy
implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
- adaptive
— Low-level building blocks such as
HingeInfo
and
Posture
- adaptive-layout
— The adaptive layouts, such as
SupportingPaneScaffold
- adaptive-navigation
— Composables for navigating within and
between panes
Create a navigator and scaffold
In small windows, only one pane displays at a time, so use a
ThreePaneScaffoldNavigator
to move to and from
panes. Create an instance of the navigator with
rememberSupportingPaneScaffoldNavigator
.
To handle back gestures, use a
BackHandler
that checks
canNavigateBack()
and calls
navigateBack()
:
val navigator = rememberSupportingPaneScaffoldNavigator()
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
}
The scaffold requires a
PaneScaffoldDirective
, which
controls how to split up the screen and how much spacing to use, and a
ThreePaneScaffoldValue
, which provides the current
state of the panes (such as whether they're expanded or hidden). For the default
behavior, use the navigator's
scaffoldDirective
and
scaffoldValue
respectively:
SupportingPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
mainPane = { /*...*/ },
supportingPane = { /*...*/ },
)
The main pane and supporting pane are composables containing your content. Use
AnimatedPane
to apply the default pane animations during
navigation. Use the scaffold value to check whether the supporting pane is
hidden; if so, display a button that calls
navigateTo(ThreePaneScaffoldRole.Secondary)
to display the
supporting pane.
Here's a complete implementation of the scaffold:
val navigator = rememberSupportingPaneScaffoldNavigator()
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
}
SupportingPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
mainPane = {
AnimatedPane(modifier = Modifier.safeContentPadding()) {
// Main pane content
if (navigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
Button(
modifier = Modifier.wrapContentSize(),
onClick = {
navigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
}
) {
Text("Show supporting pane")
}
} else {
Text("Supporting pane is shown")
}
}
},
supportingPane = {
AnimatedPane(modifier = Modifier.safeContentPadding()) {
// Supporting pane content
Text("Supporting pane")
}
},
)
Extract the individual panes of a
SupportingPaneScaffold
into their own
composables to make them reusable and testable. Use
ThreePaneScaffoldScope
to access
AnimatedPane
if
you want the default animations:
@Composable
fun ThreePaneScaffoldScope.MainPane(
shouldShowSupportingPaneButton: Boolean,
onNavigateToSupportingPane: () -> Unit,
modifier: Modifier = Modifier,
) {
AnimatedPane(modifier = modifier.safeContentPadding()) {
// Main pane content
if (shouldShowSupportingPaneButton) {
Button(onClick = onNavigateToSupportingPane) {
Text("Show supporting pane")
}
} else {
Text("Supporting pane is shown")
}
}
}
@Composable
fun ThreePaneScaffoldScope.SupportingPane(
modifier: Modifier = Modifier,
) {
AnimatedPane(modifier = modifier.safeContentPadding()) {
// Supporting pane content
Text("This is the supporting pane")
}
}
Extracting the panes into composables simplifies the use of the
SupportingPaneScaffold
(compare the following to the complete implementation
of the scaffold in the previous section):
val navigator = rememberSupportingPaneScaffoldNavigator()
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
}
SupportingPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
mainPane = {
MainPane(
shouldShowSupportingPaneButton = navigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
onNavigateToSupportingPane = { navigator.navigateTo(ThreePaneScaffoldRole.Secondary) }
)
},
supportingPane = { SupportingPane() },
)