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
andPosture
- 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 pane composables
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() }, )