(一)概述
Scaffold 是 Material Design 提供的一个页面骨架组件,用于统一管理:TopAppBar(顶部栏),BottomBar(底部栏),FloatingActionButton(悬浮按钮),Snackbar,Drawer(侧边栏),主内容区(Content)。可以把 Scaffold 理解为:Compose 页面级布局的"地基"。Scaffold可以自动处理 Insets / padding,统一了页面结构, 与 TopAppBar / SnackbarHost 深度配合。总之,Scaffold 是 Compose 页面级布局的标准入口,负责"结构",而不是"内容"。
(二)Scaffold 属性说明
Kotlin
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable (PaddingValues) -> Unit
) {
// 创建一个可变的窗口inset对象
// remember:缓存计算结果,当 contentWindowInsets 变化时重新计算
// MutableWindowInsets:可变的窗口inset,用于动态调整inset区域
val safeInsets = remember(contentWindowInsets) { MutableWindowInsets(contentWindowInsets) }
// Surface:Material Design 的表面组件,提供颜色、形状等特性
Surface(
modifier =
// 监听已经被消耗的窗口inset
modifier.onConsumedWindowInsetsChanged { consumedWindowInsets ->
// Exclude currently consumed window insets from user provided contentWindowInsets
safeInsets.insets = contentWindowInsets.exclude(consumedWindowInsets)
},
// 设置表面颜色和内容颜色
color = containerColor,
contentColor = contentColor
) {
// ScaffoldLayout:实际的布局组件,负责排列各个部分
ScaffoldLayout(
fabPosition = floatingActionButtonPosition,
topBar = topBar,
bottomBar = bottomBar,
content = content,
snackbar = snackbarHost,
contentWindowInsets = safeInsets,
fab = floatingActionButton
)
}
}
modifier - 修饰符,用于调整布局大小、位置等。
topBar - 顶部栏(如 AppBar),通常是 TopAppBar。
bottomBar - 底部栏(如 BottomNavigation)。
snackbarHost - Snackbar 容器,用于显示提示信息。
floatingActionButton - 悬浮操作按钮(FAB)。
floatingActionButtonPosition - FAB 位置,默认右侧,FabPosition.End - 右侧(RTL 布局时是左侧),FabPosition.Center - 居中。
containerColor - Scaffold 容器背景色,默认使用主题的背景色。
contentColor - 内容颜色,自动根据容器颜色计算对比色。
contentWindowInsets - 内容区域的窗口Inset(处理状态栏、导航栏等)。
content - 主要内容区域,接收 PaddingValues 来避免内容被遮挡。
(三)Scaffold 封装
Kotlin
package com.leo.wechat.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable
fun SystemBarController(
backgroundColor: Color,
contentColor: Color,
) {
// 根据内容颜色的明暗,自动判断系统状态栏 / 导航栏图标是用深色还是浅色
val darkIcons = contentColor.luminance() < 0.5f
// implementation 'com.google.accompanist:accompanist-systemuicontroller:0.32.0'
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.setStatusBarColor(
color = backgroundColor,
darkIcons = darkIcons
)
systemUiController.setNavigationBarColor(
color = backgroundColor,
darkIcons = darkIcons
)
}
}
SystemBarController.kt 说明:封装系统状态栏 / 导航栏图标是用深色还是浅色
Kotlin
package com.leo.wechat.ui.component.scaffold
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.leo.wechat.ui.component.topbar.AppTopBar
import com.leo.wechat.ui.component.topbar.AppTopBarIcon
import com.leo.wechat.utils.SystemBarController
/* StatusBar / TopBar / 页面背景 自动统一 */
@Composable
fun AppScaffold(
title: String,
modifier: Modifier = Modifier,
startIcon: AppTopBarIcon? = null,
endIcon: AppTopBarIcon? = null,
backgroundColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
content: @Composable (PaddingValues) -> Unit,
) {
SystemBarController(
backgroundColor = backgroundColor,
contentColor = contentColor
)
Scaffold(
modifier = modifier.fillMaxSize(),
topBar = {
AppTopBar(
title = title,
/**
* Modifier.statusBarsPadding() 的作用是:
* 给 Composable 自动加上"状态栏高度"的内边距,避免内容被状态栏遮挡。
* Modifier.statusBarsPadding() 等价于:Modifier.padding(top = statusBarHeight)
*/
modifier = Modifier.statusBarsPadding(),
backgroundColor = backgroundColor,
contentColor = contentColor,
startIcon = startIcon,
endIcon = endIcon
)
},
containerColor = backgroundColor
) { innerPadding ->
content(innerPadding)
}
}
AppScaffold.kt 说明:支持StatusBar / TopBar / 页面背景 自动统一,支持左右Icon按钮的TopBar
Kotlin
// 替代传统的 AppCompatActivity + XML
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 创建 Compose UI 树,替代 setContentView(R.layout.xxx),
// 内部是一个 Composable Scope(只要 State 变化,里面的 Composable 会自动重组)。
setContent {
// 控制全局主题(浅色 / 深色 等),所有 UI 都应该在 ComposeTheme 内部。
ComposeTheme {
// Context 只在需要它的最小 Composable 作用域中获取
val context = LocalContext.current
// 自定义布局架构
AppScaffold(
title = "微信",
startIcon = AppTopBarIcons.back { finish() },
endIcon = AppTopBarIcons.more {
Toast.makeText(context, "更多", Toast.LENGTH_SHORT).show()
}
) { innerPadding ->
/**
* 在自定义 AppScaffold 或 Scaffold 中:
* content 区域如果不消费 innerPadding,内容会被 layout 到 0 size 或被 top bar 覆盖。
*/
}
}
}
}
}
MainActivity.kt 说明: 主页面,使用 Compose 替代传统的 AppCompatActivity + XML 写法。