Skip to content

TopTitleScaffold — 页面骨架 ​

带标题栏的页面骨架:标题栏、三边 inset(状态栏 / 导航栏 / 输入法)、内容区底色,以及 overlay(内容铺到标题栏下面)两种模式。

这是可选的。 界面结构简单、或者要自己控制整屏布局(全屏看图、完全自定义头部、纯 Box 叠层)时,直接用 Modifier.statusBarsPadding() 这类原生手段也行 —— 前提是你清楚 inset 得自己处理。凡是要标题栏 + 返回键 + 动作区的页面,用它比手拼省事,而且不会漏 inset。

基本用法 ​

常规页面:

kotlin
TopTitleScaffold(
    title = "设置",
    onBack = { finish() },
    actions = {
        ActionImageButton(icon = Phosphor.Gear, onAction = { openSettings() })
    },
) {
    SettingsContent()
}

根页面(不画返回键):

kotlin
TopTitleScaffold(title = "首页") {
    HomeContent()
}

带副标题:

kotlin
TopTitleScaffold(
    title = "播放历史",
    subtitle = "最近 30 天",
    onBack = { finish() },
) { HistoryList() }

整屏大图 / 视频页(标题栏浮在内容上):

kotlin
TopTitleScaffold(
    title = "",
    onBack = { finish() },
    overlay = true,
    colors = TopTitleScaffoldDefaults.overlayColors(),
) {
    AsyncImage(model = url, contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize())
}

底部固定条:

kotlin
TopTitleScaffold(
    title = "作品详情",
    onBack = { finish() },
    overlay = true,
    colors = TopTitleScaffoldDefaults.overlayColors(),
    bottomBar = {
        Row(modifier = Modifier.fillMaxWidth().navigationBarsPadding()) { /* 操作栏 */ }
    },
) { /* 大图 */ }

完全自定义头部(连状态栏内缩一起接管):

kotlin
TopTitleScaffold(
    title = "",
    topBar = { MyCustomHeader() },
) { /* content */ }

参数 ​

TopTitleScaffold ​

参数类型默认值说明
titleString—标题
modifierModifierModifier修饰符
subtitleString?null标题下方那行小字,不传则不占高度
onBack(() -> Unit)?null返回回调。传 null 就不显示返回键
actions@Composable RowScope.() -> Unit{}右侧动作区,用 ActionImageButton 填
topBar(@Composable () -> Unit)?null自定义顶部栏。传了就不再用 title / subtitle / onBack / actions
bottomBar@Composable () -> Unit{}底部固定条,内容区会自动避开它
overlayBooleanfalsetrue 时内容铺满整屏、标题栏浮在其上
colorsTopTitleScaffoldColorsTopTitleScaffoldDefaults.colors()配色
content@Composable () -> Unit—页面内容。两个模式下都不用自己算 inset

TopTitleBar ​

只要标题栏、不要 Scaffold 那一层时用(比如塞进别的容器、或者标题行作列表头):

参数类型默认值说明
titleString—标题,headlineMedium
modifierModifierModifier修饰符
subtitleString?null副标题,bodySmall,空白等同于不传
onBack(() -> Unit)?null返回回调。传 null 就不显示返回键
actions@Composable RowScope.() -> Unit{}右侧动作区
colorsTopTitleScaffoldColorsTopTitleScaffoldDefaults.colors()配色
windowInsetsWindowInsetsTopAppBarDefaults.windowInsets系统装饰内缩。已自己处理状态栏时传 WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)

TopTitleScaffoldColors ​

字段colors() 默认值overlayColors() 默认值
containerColorbackgroundColor.Transparent
contentContainerColorbackgroundColor.Transparent
titleColoronSurfaceColor.White
subtitleColoronSurfaceVariantColor.White @ 70%
navigationIconTintonSurfaceColor.White
actionIconTintonSurfaceColor.White

要点 ​

  • 不要自己补 statusBarsPadding() / navigationBarsPadding()。 三边 inset 由 TopTitleScaffold 统一处理,叠着加会出现双倍留白。自己手拼布局时才需要补。
  • 返回键只在 onBack != null 时出现。 别靠传空 lambda 去「隐藏」它 —— 那样会画出一个点了没反应的返回键。根页面直接不传。
  • overlay = true 时 content 不吃 Scaffold 的 padding,contentContainerColor 那一项也不生效(容器色直接设成透明)—— 背景图自己铺满。
  • 传了 topBar 之后,状态栏内缩也归调用方管。 外层那个 statusBarsPadding() 的 Box 仍然在,但它只在不传 topBar 时才把内缩归零后交给内置标题行;自定义 topBar 会被塞进那个已经有 statusBarsPadding 的 Box 里,所以别再加一次。
  • bottomBar 自己要不要避让导航栏由调用方决定。 内容区会自动避开它,但底部条内部(如果你是自定义 Row)要自己加 Modifier.navigationBarsPadding()。
  • subtitle 不占高度:不传时标题栏就是一行。传了之后 TopAppBar 的 64dp 也放得下,不会挤成一团。
  • 右侧动作按钮用 ActionImageButton(定义在按钮那一页),它的默认图标色会通过 LocalContentColor 跟着 actionIconTint 走。

说明 ​

  • 为什么整个页面骨架都由它负责。 早期只有标题栏那一行(旧的 TopTitleBar),调用方还得自己写 Column / Box 拼「标题栏 + 内容」,Insets 一漏就是「返回键被状态栏压住」。现在归 Scaffold 管:标题栏、内容区、导航栏与输入法的内缩都在这一个组件里。
  • overlay 为什么不用另起一套 Box 布局:Scaffold 的摆放顺序是先 content、后 topBar,本来就是为了让标题栏盖在内容之上(M3 源码注释里写着这是控制绘制顺序)。所以「浮层与否」只差 content 吃不吃 padding,两个模式共用同一个 Scaffold。
  • TopTitleBar 的顶部内缩默认避让状态栏,所以单独用它时也能用;只有「自己已经处理过状态栏」或「要浮在全屏视频 / 大图上」时才传零 WindowInsets。
  • 配色没有复用 M3 的 TopAppBarColors。 后者把「容器色 / 标题色 / 图标色」绑在 TopAppBar 那套槽位上,而实际需要的是「容器透明 + 标题与返回键按图片明暗一起换色」这种组合 —— 用 TopAppBarColors 得先造一个实例再 copy 三个字段,绕。这里直接把用到的六个颜色摊平,调用方要改哪个改哪个。
  • overlayColors() 的标题与图标默认白色(看图页通常压在深色渐变上);遇到亮图时调用方把按亮度算出来的深色(例如 Color(0xFF111318))传进去即可。
  • 和 CollapsingTitleScaffold 的分工:那个是「滚动折叠」的大标题页(列表页用);这个是不折叠的静态标题栏,标题那一行高度固定。