隐式传参 (CompositionLocal)
源:使用 CompositionLocal 将数据作用域限定在局部
通常,数据通过参数向下流经 UI 树。但这对于广泛使用的数据(如颜色、字体、Context)来说很麻烦。CompositionLocal 允许您隐式地向下传递数据,而无需通过每个函数的参数。
1. 什么是 CompositionLocal?
它是 Compose 树中的一个“局部变量”。你肯定用过它们:
LocalContext.currentLocalConfiguration.currentMaterialTheme.colorScheme(底层也是 CompositionLocal)
2. 自定义 CompositionLocal
步骤 A: 创建 Key
你需要决定如果没人提供值,默认值是什么。
kotlin
// 1. staticCompositionLocalOf: 值变化时,重组整个读取它的作用域(适用于极少变化的值,如 Repository)
val LocalAnalytics = staticCompositionLocalOf<Analytics> {
error("No Analytics provided")
}
// 2. compositionLocalOf: 值变化时,只重组读取它的地方(适用于频繁变化的值,如主题颜色)
val LocalAppColors = compositionLocalOf { Color.White }步骤 B: 提供值 (Provider)
使用 CompositionLocalProvider 将值绑定到特定的子树。
kotlin
@Composable
fun App() {
val analytics = remember { AnalyticsImpl() }
CompositionLocalProvider(LocalAnalytics provides analytics) {
// 在这个作用域内,都可以通过 LocalAnalytics.current 获取到 analytics
// 而不需要通过参数一层层传下去
HomeScreen()
}
}步骤 C: 读取值
kotlin
@Composable
fun HomeScreen() {
// 隐式获取,像变魔术一样
val analytics = LocalAnalytics.current
Button(onClick = { analytics.logEvent("click") }) {
Text("Click Me")
}
}3. 最佳实践
谨慎使用
不要滥用 CompositionLocal。它使依赖关系变得隐式,导致代码难以测试和理解。
适合放入 CompositionLocal 的数据:
- 环境数据:主题、字体、语言环境。
- 系统服务:Context, Resources, WindowInfo。
- 全局单例:Analytics, Logger, Dependency Injection Container (如 Hilt 的 EntryPoint)。
不适合的数据:
- ViewModel:应该通过参数显式传递。
- 业务数据:如 User 对象、List 数据。