自适应布局
自适应布局 · androidx.window
ListDetailPaneScaffold:列表与详情自动分合Window 1.6.0-alpha05
用 navigator 保留目的地,用 scaffold 根据窗口决定 pane 数量。
最后更新 2026-08-01
复制即用
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun MailScreen() {
val scope = rememberCoroutineScope()
val navigator = rememberListDetailPaneScaffoldNavigator<String>()
ListDetailPaneScaffold(
directive = navigator.scaffoldDirective,
scaffoldState = navigator.scaffoldState,
listPane = {
AnimatedPane {
MailList(onOpen = { id ->
scope.launch {
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, id)
}
})
}
},
detailPane = {
AnimatedPane {
MailDetail(navigator.currentDestination?.contentKey)
}
},
)
}两个角色
| 角色 | 职责 |
|---|---|
| Navigator | 保存当前 pane 目的地、计算 scaffold 状态、响应导航 |
| Scaffold | 按 directive 把 list/detail/extra pane 放好 |
核心概念:“选择了哪一项”必须独立于”当前显示几个 pane”:
- 窄窗口:只显示当前 pane(详情覆盖列表)。
- 宽窗口:同时显示列表 + 详情。
- 状态由 navigator 管理,窗口变化不丢选择。
导航与返回
// 打开详情
scope.launch { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, itemId) }
// 系统返回
BackHandler {
if (navigator.canNavigateBack()) {
navigator.navigateBack() // 先:scaffold 内从详情回列表
} else {
// 再:交给应用外层 back stack
onExit()
}
}canNavigateBack() 判断 scaffold 内能否返回(如详情回列表);能就先 navigateBack(),不能才交给外层。
SupportingPaneScaffold:主 + 辅助
需要”主内容 + 辅助内容”(如详情页里的辅助面板)时:
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun SupportingScaffold() {
val navigator = rememberSupportingPaneScaffoldNavigator<Int>()
SupportingPaneScaffold(
directive = navigator.scaffoldDirective,
scaffoldState = navigator.scaffoldState,
mainPane = { MainPane() },
supportingPane = { SupportingPane() },
)
}常见陷阱
- 选择状态和 pane 数量耦合:窄屏详情覆盖列表后返回丢选择。
- 返回处理不完整:不检查
canNavigateBack()直接退出整个页面。 - 忘 AnimatedPane:pane 切换无动画/布局突变。
- 实验 API:当前版本带
ExperimentalMaterial3AdaptiveApi,升级 alpha 重核对。
复制即用
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ListDetailExample() {
val scope = rememberCoroutineScope()
val navigator = rememberListDetailPaneScaffoldNavigator<String>()
BackHandler(enabled = navigator.canNavigateBack()) {
navigator.navigateBack()
}
ListDetailPaneScaffold(
directive = navigator.scaffoldDirective,
scaffoldState = navigator.scaffoldState,
listPane = {
AnimatedPane { ItemList(onSelect = { id ->
scope.launch { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, id) }
}) }
},
detailPane = {
AnimatedPane { ItemDetail(navigator.currentDestination?.contentKey) }
},
)
}要点
- Navigator 管状态,Scaffold 管布局;选择独立于 pane 数量。
- 返回先
canNavigateBack()→navigateBack(),再交外层。 - 辅助内容用 SupportingPaneScaffold。
- 实验 API,升级时重核对签名。