androidx.compose.material3.adaptive.layout


Interfaces

AdaptStrategy

Provides the information about how the associated pane should be adapted if it cannot be displayed in its PaneAdaptedValue.Expanded state.

Cmn
AnimatedPaneScope

Scope for the content of AnimatedPane.

Cmn
PaneScaffoldScope

Scope for the panes of pane scaffolds.

Cmn
ThreePaneScaffoldScope

Scope for the panes of ThreePaneScaffold.

Cmn

Classes

HingePolicy

Policies that indicate how hinges are supposed to be addressed in an adaptive layout.

Cmn
PaneAdaptedValue

The adapted state of a pane.

Cmn
PaneScaffoldDirective

Top-level directives about how a pane scaffold should be arranged and spaced, like how many partitions the layout can be split into and what should be the gutter size.

Cmn
ThreePaneScaffoldAdaptStrategies

The adaptation specs of ThreePaneScaffold.

Cmn
ThreePaneScaffoldDestinationItem

An item representing a navigation destination in a ThreePaneScaffold.

Cmn
ThreePaneScaffoldValue

The adapted value of ThreePaneScaffold.

Cmn

Objects

ListDetailPaneScaffoldDefaults

Provides default values of ListDetailPaneScaffold.

Cmn
ListDetailPaneScaffoldRole

The set of the available pane roles of ListDetailPaneScaffold.

Cmn
SupportingPaneScaffoldDefaults

Provides default values of SupportingPaneScaffold.

Cmn
SupportingPaneScaffoldRole

The set of the available pane roles of SupportingPaneScaffold.

Cmn

Enums

ThreePaneScaffoldRole

The set of the available pane roles of ThreePaneScaffold.

Cmn

Top-level functions summary

Unit
@ExperimentalMaterial3AdaptiveApi
@Composable
ListDetailPaneScaffold(
    directive: PaneScaffoldDirective,
    value: ThreePaneScaffoldValue,
    listPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    detailPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    modifier: Modifier,
    extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)?
)

A Material opinionated implementation of ThreePaneScaffold that will display the provided three panes in a canonical list-detail layout.

Cmn
Unit
@ExperimentalMaterial3AdaptiveApi
@Composable
SupportingPaneScaffold(
    directive: PaneScaffoldDirective,
    value: ThreePaneScaffoldValue,
    mainPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    supportingPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    modifier: Modifier,
    extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)?
)

A Material opinionated implementation of ThreePaneScaffold that will display the provided three panes in a canonical supporting-pane layout.

Cmn
PaneScaffoldDirective
@ExperimentalMaterial3AdaptiveApi
calculatePaneScaffoldDirective(
    windowAdaptiveInfo: WindowAdaptiveInfo,
    verticalHingePolicy: HingePolicy
)

Calculates the recommended PaneScaffoldDirective from a given WindowAdaptiveInfo.

Cmn
PaneScaffoldDirective

Calculates the recommended PaneScaffoldDirective from a given WindowAdaptiveInfo.

Cmn
ThreePaneScaffoldValue
@ExperimentalMaterial3AdaptiveApi
calculateThreePaneScaffoldValue(
    maxHorizontalPartitions: Int,
    adaptStrategies: ThreePaneScaffoldAdaptStrategies,
    currentDestination: ThreePaneScaffoldDestinationItem<*>?
)

Calculates the current adapted value of ThreePaneScaffold according to the given maxHorizontalPartitions, adaptStrategies and currentDestination.

Cmn
ThreePaneScaffoldValue
@ExperimentalMaterial3AdaptiveApi
calculateThreePaneScaffoldValue(
    maxHorizontalPartitions: Int,
    adaptStrategies: ThreePaneScaffoldAdaptStrategies,
    destinationHistory: List<ThreePaneScaffoldDestinationItem<*>>
)

Calculates the current adapted value of ThreePaneScaffold according to the given maxHorizontalPartitions, adaptStrategies and destinationHistory.

Cmn

Extension functions summary

Unit

The root composable of pane contents in a ThreePaneScaffold that supports default motions during pane switching.

Cmn

Top-level functions

ListDetailPaneScaffold

@ExperimentalMaterial3AdaptiveApi
@Composable
fun ListDetailPaneScaffold(
    directive: PaneScaffoldDirective,
    value: ThreePaneScaffoldValue,
    listPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    detailPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    modifier: Modifier = Modifier,
    extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null
): Unit

A Material opinionated implementation of ThreePaneScaffold that will display the provided three panes in a canonical list-detail layout.

See usage samples at:

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator()
ListDetailPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    value = scaffoldNavigator.scaffoldValue,
    listPane = {
        AnimatedPane(
            modifier = Modifier.preferredWidth(200.dp),
        ) {
            Surface(
                color = MaterialTheme.colorScheme.secondary,
                onClick = {
                    scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                }
            ) {
                Text("List")
            }
        }
    },
    detailPane = {
        AnimatedPane(modifier = Modifier) {
            Surface(
                color = MaterialTheme.colorScheme.primary,
                onClick = {
                    scaffoldNavigator.navigateBack()
                }
            ) {
                Text("Details")
            }
        }
    }
)
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator()
ListDetailPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    value = scaffoldNavigator.scaffoldValue,
    listPane = {
        AnimatedPane(
            modifier = Modifier.preferredWidth(200.dp),
        ) {
            Surface(
                color = MaterialTheme.colorScheme.secondary,
                onClick = {
                    scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                }
            ) {
                Text("List")
            }
        }
    },
    detailPane = {
        AnimatedPane(
            modifier = Modifier
        ) {
            Surface(
                color = MaterialTheme.colorScheme.primary,
            ) {
                Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
                    Text("Detail")
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 4.dp),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        Surface(
                            onClick = {
                                scaffoldNavigator.navigateBack()
                            },
                            modifier = Modifier
                                .weight(0.5f)
                                .fillMaxHeight(),
                            color = MaterialTheme.colorScheme.primary.copy(alpha = 0.8f)
                        ) {
                            Box(
                                modifier = Modifier.fillMaxSize(),
                                contentAlignment = Alignment.Center
                            ) {
                                Text("Previous")
                            }
                        }
                        VerticalDivider()
                        Surface(
                            onClick = {
                                scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Extra)
                            },
                            modifier = Modifier
                                .weight(0.5f)
                                .fillMaxHeight(),
                            color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)
                        ) {
                            Box(
                                modifier = Modifier.fillMaxSize(),
                                contentAlignment = Alignment.Center
                            ) {
                                Text("Next")
                            }
                        }
                    }
                }
            }
        }
    },
    extraPane = {
        AnimatedPane(
            modifier = Modifier.fillMaxSize()
        ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.tertiary,
                onClick = {
                    scaffoldNavigator.navigateBack()
                }
            ) {
                Text("Extra")
            }
        }
    }
)
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
import androidx.compose.material3.adaptive.navigation.BackNavigationBehavior
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

fun ThreePaneScaffoldNavigator<*>.isListExpanded() =
    scaffoldValue[ListDetailPaneScaffoldRole.List] == PaneAdaptedValue.Expanded
fun ThreePaneScaffoldNavigator<*>.isDetailExpanded() =
    scaffoldValue[ListDetailPaneScaffoldRole.Detail] == PaneAdaptedValue.Expanded
val welcomeRoute = "welcome"
val listDetailRoute = "listdetail"
val items = List(15) { "Item $it" }
val loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " +
    "tempor incididunt ut labore et dolore magna aliqua. Dui nunc mattis enim ut tellus " +
    "elementum sagittis. Nunc sed augue lacus viverra vitae. Sit amet dictum sit amet justo " +
    "donec. Fringilla urna porttitor rhoncus dolor purus non enim praesent elementum."

@Composable
fun ListCard(
    title: String,
    highlight: Boolean,
    modifier: Modifier = Modifier,
) {
    OutlinedCard(
        colors = CardDefaults.outlinedCardColors(when {
            highlight -> MaterialTheme.colorScheme.surfaceVariant
            else -> MaterialTheme.colorScheme.surface
        }),
        modifier = modifier
            .heightIn(min = 72.dp)
            .fillMaxWidth(),
    ) {
        Text(
            text = title,
            modifier = Modifier.padding(8.dp),
            style = MaterialTheme.typography.headlineLarge,
        )
    }
}

@Composable
fun DetailScreen(
    title: String,
    details: String,
    backButton: @Composable () -> Unit,
    modifier: Modifier = Modifier,
) {
    Scaffold(
        modifier = modifier,
        topBar = { TopAppBar(title = { Text(title) }, navigationIcon = backButton) },
    ) { paddingValues ->
        Card(
            colors = CardDefaults.cardColors(MaterialTheme.colorScheme.surfaceVariant),
            modifier = Modifier
                .padding(paddingValues)
                .fillMaxSize(),
        ) {
            Text(
                text = details,
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.bodyMedium,
            )
        }
    }
}

// `navController` handles navigation outside the ListDetailPaneScaffold,
// and `scaffoldNavigator` handles navigation within it. The "content" of
// the scaffold uses String ids, which we pass as a type argument to
// `rememberListDetailPaneScaffoldNavigator`. If you don't need the
// scaffold navigator to be aware of its content, you can pass `Nothing`.
val navController = rememberNavController()
val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<String>()

NavHost(
    navController = navController,
    startDestination = welcomeRoute,
    enterTransition = { slideInHorizontally(initialOffsetX = { it }) },
    exitTransition = { slideOutHorizontally(targetOffsetX = { -it }) },
    popEnterTransition = { slideInHorizontally(initialOffsetX = { -it }) },
    popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) },
) {
    composable(welcomeRoute) {
        Scaffold(Modifier.fillMaxSize()) { paddingValues ->
            Box(
                Modifier
                    .padding(paddingValues)
                    .fillMaxSize()) {
                Text(
                    text = "Welcome Screen",
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .padding(top = 24.dp),
                    style = MaterialTheme.typography.displayMedium,
                )
                Button(
                    onClick = { navController.navigate(listDetailRoute) },
                    modifier = Modifier.align(Alignment.Center),
                ) {
                    Text("Next")
                }
            }
        }
    }
    composable(listDetailRoute) {
        val listScrollState = rememberScrollState()
        val selectedItem = scaffoldNavigator.currentDestination?.content

        // Back behavior can be customized based on the scaffold's layout.
        // In this example, back navigation goes item-by-item when both
        // list and detail panes are expanded. But if only one pane is
        // showing, back navigation goes from detail screen to list-screen.
        val backBehavior =
            if (scaffoldNavigator.isListExpanded() && scaffoldNavigator.isDetailExpanded()) {
                BackNavigationBehavior.PopUntilContentChange
            } else {
                BackNavigationBehavior.PopUntilScaffoldValueChange
            }

        BackHandler(enabled = scaffoldNavigator.canNavigateBack(backBehavior)) {
            scaffoldNavigator.navigateBack(backBehavior)
        }

        ListDetailPaneScaffold(
            directive = scaffoldNavigator.scaffoldDirective,
            value = scaffoldNavigator.scaffoldValue,
            listPane = {
                AnimatedPane(Modifier.preferredWidth(240.dp)) {
                    Surface {
                        Column(
                            modifier = Modifier.verticalScroll(listScrollState),
                            verticalArrangement = Arrangement.spacedBy(4.dp),
                        ) {
                            items.forEach { item ->
                                ListCard(
                                    title = item,
                                    highlight = item == selectedItem &&
                                        scaffoldNavigator.isDetailExpanded(),
                                    modifier = Modifier.clickable {
                                        if (item != selectedItem) {
                                            scaffoldNavigator.navigateTo(
                                                pane = ListDetailPaneScaffoldRole.Detail,
                                                content = item,
                                            )
                                        }
                                    }
                                )
                            }
                        }
                    }
                }
            },
            detailPane = {
                AnimatedPane {
                    Crossfade(
                        targetState = selectedItem,
                        label = "Detail Pane",
                    ) { item ->
                        val title = item ?: "No item selected"
                        val details =
                            if (item != null) loremIpsum else "Select an item from the list"

                        DetailScreen(
                            title = title,
                            details = details,
                            backButton = {
                                AnimatedVisibility(
                                    visible = !scaffoldNavigator.isListExpanded()
                                ) {
                                    IconButton(
                                        onClick = {
                                            scaffoldNavigator.navigateBack(backBehavior)
                                        },
                                        content = {
                                            Icon(Icons.AutoMirrored.Filled.ArrowBack, null)
                                        }
                                    )
                                }
                            }
                        )
                    }
                }
            },
        )
    }
}
Parameters
directive: PaneScaffoldDirective

The top-level directives about how the scaffold should arrange its panes.

value: ThreePaneScaffoldValue

The current adapted value of the scaffold, which indicates how each pane of the scaffold is adapted.

listPane: @Composable ThreePaneScaffoldScope.() -> Unit

the list pane of the scaffold, which is supposed to hold a list of item summaries that can be selected from, for example, the inbox mail list of a mail app. See ListDetailPaneScaffoldRole.List.

detailPane: @Composable ThreePaneScaffoldScope.() -> Unit

the detail pane of the scaffold, which is supposed to hold the detailed info of a selected item, for example, the mail content currently being viewed. See ListDetailPaneScaffoldRole.Detail.

modifier: Modifier = Modifier

Modifier of the scaffold layout.

extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null

the extra pane of the scaffold, which is supposed to hold any supplementary info besides the list and the detail panes, for example, a task list or a mini-calendar view of a mail app. See ListDetailPaneScaffoldRole.Extra.

SupportingPaneScaffold

@ExperimentalMaterial3AdaptiveApi
@Composable
fun SupportingPaneScaffold(
    directive: PaneScaffoldDirective,
    value: ThreePaneScaffoldValue,
    mainPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    supportingPane: @Composable ThreePaneScaffoldScope.() -> Unit,
    modifier: Modifier = Modifier,
    extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null
): Unit

A Material opinionated implementation of ThreePaneScaffold that will display the provided three panes in a canonical supporting-pane layout.

Parameters
directive: PaneScaffoldDirective

The top-level directives about how the scaffold should arrange its panes.

value: ThreePaneScaffoldValue

The current adapted value of the scaffold, which indicates how each pane of the scaffold is adapted.

mainPane: @Composable ThreePaneScaffoldScope.() -> Unit

the main pane of the scaffold, which is supposed to hold the major content of an app, for example, the editing screen of a doc app. See SupportingPaneScaffoldRole.Main.

supportingPane: @Composable ThreePaneScaffoldScope.() -> Unit

the supporting pane of the scaffold, which is supposed to hold the support content of an app, for example, the comment list of a doc app. See SupportingPaneScaffoldRole.Supporting.

modifier: Modifier = Modifier

Modifier of the scaffold layout.

extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null

the extra pane of the scaffold, which is supposed to hold any additional content besides the main and the supporting panes, for example, a styling panel in a doc app. See SupportingPaneScaffoldRole.Extra.

calculatePaneScaffoldDirective

@ExperimentalMaterial3AdaptiveApi
fun calculatePaneScaffoldDirective(
    windowAdaptiveInfo: WindowAdaptiveInfo,
    verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
): PaneScaffoldDirective

Calculates the recommended PaneScaffoldDirective from a given WindowAdaptiveInfo. Use this method with currentWindowAdaptiveInfo to acquire Material-recommended adaptive layout settings of the current activity window.

See more details on the Material design guideline site (https://m3.material.io/foundations/layout/applying-layout/window-size-classes).

Parameters
windowAdaptiveInfo: WindowAdaptiveInfo

WindowAdaptiveInfo that collects useful information in making layout adaptation decisions like WindowSizeClass.

verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating

HingePolicy that decides how layouts are supposed to address vertical hinges.

Returns
PaneScaffoldDirective

an PaneScaffoldDirective to be used to decide adaptive layout states.

calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth

@ExperimentalMaterial3AdaptiveApi
fun calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
    windowAdaptiveInfo: WindowAdaptiveInfo,
    verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
): PaneScaffoldDirective

Calculates the recommended PaneScaffoldDirective from a given WindowAdaptiveInfo. Use this method with currentWindowAdaptiveInfo to acquire Material-recommended dense-mode adaptive layout settings of the current activity window. Note that this function results in a dual-pane layout when the WindowWidthSizeClass is WindowWidthSizeClass.MEDIUM, while calculatePaneScaffoldDirective results in a single-pane layout instead. We recommend to use calculatePaneScaffoldDirective, unless you have a strong use case to show two panes on a medium-width window, which can make your layout look too packed.

See more details on the Material design guideline site (https://m3.material.io/foundations/layout/applying-layout/window-size-classes).

Parameters
windowAdaptiveInfo: WindowAdaptiveInfo

WindowAdaptiveInfo that collects useful information in making layout adaptation decisions like WindowSizeClass.

verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating

HingePolicy that decides how layouts are supposed to address vertical hinges.

Returns
PaneScaffoldDirective

an PaneScaffoldDirective to be used to decide adaptive layout states.

calculateThreePaneScaffoldValue

@ExperimentalMaterial3AdaptiveApi
fun calculateThreePaneScaffoldValue(
    maxHorizontalPartitions: Int,
    adaptStrategies: ThreePaneScaffoldAdaptStrategies,
    currentDestination: ThreePaneScaffoldDestinationItem<*>?
): ThreePaneScaffoldValue

Calculates the current adapted value of ThreePaneScaffold according to the given maxHorizontalPartitions, adaptStrategies and currentDestination. The returned value can be used as a unique representation of the current layout structure.

The function will treat the current destination as the highest priority and then adapt the rest panes according to the order of ThreePaneScaffoldRole.Primary, ThreePaneScaffoldRole.Secondary and ThreePaneScaffoldRole.Tertiary. If there are still remaining partitions to put the pane, the pane will be set as PaneAdaptedValue.Expanded, otherwise it will be adapted according to its associated AdaptStrategy.

Parameters
maxHorizontalPartitions: Int

The maximum allowed partitions along the horizontal axis, i.e., how many expanded panes can be shown at the same time.

adaptStrategies: ThreePaneScaffoldAdaptStrategies

The adapt strategies of each pane role that ThreePaneScaffold supports, the default value will be ThreePaneScaffoldDefaults.threePaneScaffoldAdaptStrategies.

currentDestination: ThreePaneScaffoldDestinationItem<*>?

The current destination item, which will be treated as having the highest priority, can be null.

calculateThreePaneScaffoldValue

@ExperimentalMaterial3AdaptiveApi
fun calculateThreePaneScaffoldValue(
    maxHorizontalPartitions: Int,
    adaptStrategies: ThreePaneScaffoldAdaptStrategies,
    destinationHistory: List<ThreePaneScaffoldDestinationItem<*>>
): ThreePaneScaffoldValue

Calculates the current adapted value of ThreePaneScaffold according to the given maxHorizontalPartitions, adaptStrategies and destinationHistory. The returned value can be used as a unique representation of the current layout structure.

The function will treat the current focus as the highest priority and then adapt the rest panes according to the order of ThreePaneScaffoldRole.Primary, ThreePaneScaffoldRole.Secondary and ThreePaneScaffoldRole.Tertiary. If there are still remaining partitions to put the pane, the pane will be set as PaneAdaptedValue.Expanded, otherwise it will be adapted according to its associated AdaptStrategy.

Parameters
maxHorizontalPartitions: Int

The maximum allowed partitions along the horizontal axis, i.e., how many expanded panes can be shown at the same time.

adaptStrategies: ThreePaneScaffoldAdaptStrategies

The adapt strategies of each pane role that ThreePaneScaffold supports, the default value will be ThreePaneScaffoldDefaults.threePaneScaffoldAdaptStrategies.

destinationHistory: List<ThreePaneScaffoldDestinationItem<*>>

The history of past destination items. The last destination will have the highest priority, and the second last destination will have the second highest priority, and so forth until all panes have a priority assigned. Note that the last destination is supposed to be the last item of the provided list.

Extension functions

@ExperimentalMaterial3AdaptiveApi
@Composable
fun ThreePaneScaffoldScope.AnimatedPane(
    modifier: Modifier = Modifier,
    content: @Composable AnimatedPaneScope.() -> Unit
): Unit

The root composable of pane contents in a ThreePaneScaffold that supports default motions during pane switching. It's recommended to use this composable to wrap your own contents when passing them into pane parameters of the scaffold functions, therefore your panes can have a nice default animation for free.

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator()
ListDetailPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    value = scaffoldNavigator.scaffoldValue,
    listPane = {
        AnimatedPane(
            modifier = Modifier.preferredWidth(200.dp),
        ) {
            Surface(
                color = MaterialTheme.colorScheme.secondary,
                onClick = {
                    scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                }
            ) {
                Text("List")
            }
        }
    },
    detailPane = {
        AnimatedPane(modifier = Modifier) {
            Surface(
                color = MaterialTheme.colorScheme.primary,
                onClick = {
                    scaffoldNavigator.navigateBack()
                }
            ) {
                Text("Details")
            }
        }
    }
)
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator()
ListDetailPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    value = scaffoldNavigator.scaffoldValue,
    listPane = {
        AnimatedPane(
            modifier = Modifier.preferredWidth(200.dp),
        ) {
            Surface(
                color = MaterialTheme.colorScheme.secondary,
                onClick = {
                    scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                }
            ) {
                Text("List")
            }
        }
    },
    detailPane = {
        AnimatedPane(
            modifier = Modifier
        ) {
            Surface(
                color = MaterialTheme.colorScheme.primary,
            ) {
                Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
                    Text("Detail")
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 4.dp),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        Surface(
                            onClick = {
                                scaffoldNavigator.navigateBack()
                            },
                            modifier = Modifier
                                .weight(0.5f)
                                .fillMaxHeight(),
                            color = MaterialTheme.colorScheme.primary.copy(alpha = 0.8f)
                        ) {
                            Box(
                                modifier = Modifier.fillMaxSize(),
                                contentAlignment = Alignment.Center
                            ) {
                                Text("Previous")
                            }
                        }
                        VerticalDivider()
                        Surface(
                            onClick = {
                                scaffoldNavigator.navigateTo(ListDetailPaneScaffoldRole.Extra)
                            },
                            modifier = Modifier
                                .weight(0.5f)
                                .fillMaxHeight(),
                            color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)
                        ) {
                            Box(
                                modifier = Modifier.fillMaxSize(),
                                contentAlignment = Alignment.Center
                            ) {
                                Text("Next")
                            }
                        }
                    }
                }
            }
        }
    },
    extraPane = {
        AnimatedPane(
            modifier = Modifier.fillMaxSize()
        ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.tertiary,
                onClick = {
                    scaffoldNavigator.navigateBack()
                }
            ) {
                Text("Extra")
            }
        }
    }
)
Parameters
modifier: Modifier = Modifier

The modifier applied to the AnimatedPane.

content: @Composable AnimatedPaneScope.() -> Unit

The content of the AnimatedPane. Also see AnimatedPaneScope.

See usage samples at: