ComposedModifier:深入理解与实践指南
ComposedModifier 基础介绍
ComposedModifier是一个特殊的Modifier,它实现了Element接口。
kotlin
// ComposedModifier.kt
private open class ComposedModifier(
inspectorInfo: InspectorInfo.() -> Unit,
val factory: @Composable Modifier.() -> Modifier // 具有@Composable上下文环境,接收者类型是Modifier,返回一个Modifier
) : Modifier.Element, InspectorValueInfo(inspectorInfo)
ComposedModifier
本身不提供任何的实际功能,也就是对界面没有作用。它的作用是将我们想要提供的Modifier
包装在一个工厂函数factory
里面,在界面组合时,会去执行这个工厂函数,获取实际的Modifier
对象。
它的核心特点是在组合过程中动态创建 Modifier,而不是在声明时立即创建。
ComposedModifier 的工作原理
创建与调用流程
使用ComposedModifier特别简单,调用composed()
函数即可。
kotlin
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
像下面这样:
kotlin
Box(
modifier = Modifier.composed {
Modifier.padding(8.dp)
}
)
那工厂函数factory
具体的调用时机是什么?
比如在上面的代码中,会先调用composed
函数,创建ComposedModifier
实例,但此时工厂函数还没有被调用,当包含了ComposedModifier
的组件Box
执行时,就会去调用工厂函数,返回实际的Modifier
对象。
源码解析
找一下调用的源码,首先进入Box函数:
kotlin
// Box.kt
@Composable
fun Box(modifier: Modifier) {
Layout(measurePolicy = EmptyBoxMeasurePolicy, modifier = modifier) // 进入Layout
}
然后,进入 Layout 函数:
kotlin
// Layout.kt
@Composable
@UiComposable
inline fun Layout(
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val compositeKeyHash = currentCompositeKeyHash
val materialized = currentComposer.materialize(modifier) // 进入materialize
// ..
}
最后,进到 materialize
函数中:
kotlin
// ComoposedModifier.kt
fun Composer.materialize(modifier: Modifier): Modifier {
if (modifier.all { it !is ComposedModifier }) {
return modifier
}
// ..
val result = modifier.foldIn<Modifier>(Modifier) { acc, element ->
acc.then(
if (element is ComposedModifier) {
@Suppress("UNCHECKED_CAST")
val factory = element.factory as Modifier.(Composer, Int) -> Modifier
val composedMod = factory(Modifier, this, 0)
materialize(composedMod)
} else {
element
}
)
}
endReplaceableGroup()
return result
}
就是在 materialize
函数中处理 ComposedModifier的。
在 materialize
函数中,会先判断当前可组合函数的Modifier链是否包含ComposedModifier
,如果不包含直接返回。
包含的话,会遍历Modifier链,遇到ComposedModifier
类型的元素,就调用它的factory
工厂函数,将得到的结果作为materialize
函数的参数,再次调用materialize
函数(也就是递归处理结果);遇到非ComposedModifier
,就直接添加到结果链中。
简单来说,就是将Modifier链中所有ComposedModifier类型的Modifier,调用它的工厂函数factory
,变为非ComposedModifier类型的Modifier。
比如这个Modifier链:
kotlin
modifier = Modifier
.size(100.dp)
.composed {
Modifier.padding(8.dp)
}
.background(Color.Green)
处理过程:

这下我们知道了ComposedModifier
的工厂函数factory
,确实会在组合的过程中被执行。
有状态 Modifier 与状态隔离
有状态 Modifier
那么这个composed()
函数的作用就只是延迟创建Modifier实例而已吗?
我们先来看看这个函数的注释:
sql
Declare a just-in-time composition of a Modifier that will be composed for each element it modifies.
声明一个 Modifier 的即时组合,该组合将为它修饰的每个元素单独执行。
composed may be used to implement stateful modifiers that have instance-specific state for each modified element, allowing the same Modifier instance to be safely reused for multiple elements while maintaining element-specific state.
composed 可用于实现有状态的修饰符,这些修饰符为每个被修饰的元素维护特定的实例状态,从而允许同一个 Modifier 实例安全地被多个元素重用,同时保持元素特定的状态。
关键术语解释:
- 即时组合(just-in-time composition) :指Modifier不是在声明时立即创建,而是在组合过程中根据需要动态创建
- 元素特定的实例状态(instance-specific state for each modified element) :每个使用该Modifier的组件都会获得自己独立的状态实例
- 安全重用(safely reused) :同一个Modifier的定义,可以用在多个不同的组件中,而不会导致状态混淆或冲突
看完后,你可能还是很疑惑,别担心,结合代码理解。
你只是这样写的话:
kotlin
// 写法1:composed()
val modifier = Modifier.composed {
Modifier.padding(8.dp)
}
Box(modifier = modifier)
Text(text = "Hello World!", modifier = modifier)
// 写法2:正常创建Modifier
val modifier1 = Modifier.padding(8.dp)
Box(modifier1)
Text(text = "Hello World!", modifier = modifier1)
那这两种写法的唯一区别就是:Modifier实例的创建时间,对界面显示没有任何影响。
所以它的使用场景不在于这, 而是用于有状态的Modifier的。
那什么是有状态 Modifier?
这里的状态和Composable函数中的状态是一个意思,是指包含内部状态(如 mutableStateOf
)的 Modifier。这些状态可以影响 Modifier 的行为和外观。
比如这样写,Modifier就有了状态。
kotlin
val modifier = Modifier.composed {
// 声明状态
var isActive by remember { mutableStateOf(false) }
// 基于状态返回不同的修饰符
Modifier
.clickable { isActive = !isActive }
.background(if (isActive) Color.Red else Color.Green)
}
我们可以在这个Modifier里面维护内部状态(管理状态),并且每一个使用了这个Modifier的元素,都有自己独立的状态实例,我们可以将我们的Modifier,用在多个地方。
但是实际开发中,为了代码的可读性、可维护性,不会这样写的,而是这样写:
kotlin
// 声明状态
var isActive by remember { mutableStateOf(false) }
// 基于状态返回不同的修饰符
val modifier = Modifier
.clickable { isActive = !isActive }
.background(if (isActive) Color.Red else Color.Green)
所以这种有状态的Modifier 的使用场景,也不在这,而是在于自定义Modifier。
比如这样:
kotlin
// 创建自定义Modifier
@SuppressLint("ModifierFactoryUnreferencedReceiver")
fun Modifier.myStatefulModifier(): Modifier = composed {
// 声明状态
var isActive by remember { mutableStateOf(false) }
// 基于状态返回不同的修饰符
Modifier
.clickable { isActive = !isActive }
.background(if (isActive) Color.Red else Color.Green)
}
// 使用自定义Modifier
Box(Modifier
.size(100.dp)
.myStatefulModifier())

状态隔离
自定义Modifier时,使用 composed 就可以确保状态隔离,如果不使用 composed()
(不状态隔离),当多个组件使用同一个 Modifier 定义时,这些组件会共享同一个状态实例,导致意外的情况发生:
kotlin
fun Modifier.incorrectStatefulModifier(): Modifier {
// 状态会被所有使用此修饰符的元素共享
var isActive by mutableStateOf(false)
return this
.clickable { isActive = !isActive }
.background(if (isActive) Color.Red else Color.Green)
}
如果有多个元素应用了上面的Modifier,点击任何一个元素都会改变所有元素的背景颜色,因为它们共享同一个 isActive
状态。
ComposedModifier 的使用场景
需要状态管理的自定义 Modifier
上面有例子。
读取 CompositionLocal 值
由于工厂函数factory
具有 @Composable
上下文,所以可以访问当前组合上下文中的 CompositionLocal 值,比如访问当前主题颜色:
kotlin
fun Modifier.themeDependentBackground(): Modifier = composed {
// 访问当前主题颜色
val primaryColor = MaterialTheme.colorScheme.primary
val surfaceColor = MaterialTheme.colorScheme.surface
// 使用主题颜色创建渐变背景
this.background(
brush = Brush.linearGradient(
colors = listOf(primaryColor, surfaceColor)
)
)
}
访问当前的 Context 或其他上下文信息:
kotlin
// 获取上下文
val context = LocalContext.current
调用Composable 函数
在 composed()
中可以调用其他 Composable 函数(原因同上),如 LaunchedEffect
、DisposableEffect
等:
点击后旋转的Modifier
我们现在来实现一个自定义的有状态 Modifier,效果:当点击元素后,会开始旋转,再次点击停止旋转。
其实有一个和这个类似的效果,就是掘金个人主页中的头像动画效果:把鼠标悬停在头像上,头像会开始旋转,并且然后越来越快,最后匀速。
效果如下:

我们实现的效果和他是不同的,只是提一下,那开始实现吧,先搞定旋转。我们创建一个无终止的动画,然后设置元素的旋转角度即可。
注意:修改动画时长,即可修改旋转速度。
kotlin
fun Modifier.CustomRotate(): Modifier = composed {
// 创建无限动画
val infiniteTransition = rememberInfiniteTransition(label = "rotateOnHover")
// 旋转角度
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 3000, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "rotation"
)
// 旋转
rotate(rotation)
}
效果:

可以看到已经可以旋转了。
再来完成点击后才开始旋转:根据是否在旋转,来设置不同的目标角度。
kotlin
@SuppressLint("ModifierFactoryUnreferencedReceiver") // Modifier 扩展函数中不使用 `this` 接收者,就需要加上这个注解
fun Modifier.rotateOnClick(): Modifier = composed {
// 是否旋转
var isRotating by remember { mutableStateOf(false) }
// 创建无限旋转的动画
val infiniteTransition = rememberInfiniteTransition(label = "rotateOnHover")
// 初始值和目标值
val initialValue = 0f
val targetValue = if (isRotating) 360f else 0f
// 旋转角度
val rotation by infiniteTransition.animateFloat(
initialValue = initialValue,
targetValue = targetValue,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 3000, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "rotation"
)
// 应用点击事件和旋转效果
clickable {
isRotating = !isRotating
}.rotate(rotation)
}
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Box(
Modifier
.size(100.dp)
.rotateOnClick()
.background(Color.Green)
)
}
效果:

可以看到点击后,就会开始旋转了。
最佳实践
何时使用 composed()
就是ComposedModifier 的使用场景:
- 需要状态管理:当自定义 Modifier 需要维护内部状态时
- 需要访问 Composable 上下文:当需要访问 CompositionLocal、Context 或其他上下文信息时
- 需要使用其他 Composable 函数:当需要使用 LaunchedEffect、DisposableEffect 等函数时
- 需要使用动画 API:当需要使用 animateXxxAsState 或其他动画 API 时
其中第二点,我觉得是最重要的。
避免过度使用
不要在所有自定义 Modifier 中都使用 composed()
,只在必要时使用。过度使用会增加组合成本和内存使用。
composed() 不再推荐使用
主要由于性能问题(特别是在频繁重组的情况下),Google 已经不再推荐使用 composed() 方法了。
替代方案有:
- 使用带有 @Composable 注解的 Modifier 扩展函数
- 使用Modifier.Node API
示例比较: 使用 composed()
kotlin
// 使用 composed() 的方式
fun Modifier.fade(enable: Boolean): Modifier = composed {
val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)
graphicsLayer {
this.alpha = alpha
}
}
使用带有 @Composable 注解的 Modifier 扩展函数
kotlin
@Composable
fun Modifier.fade(enable: Boolean): Modifier {
val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)
return graphicsLayer {
this.alpha = alpha
}
}
使用Modifier.Node API
kotlin
// 创建自定义 Modifier
class FadeModifierNode(
var enable: Boolean
) : ModifierNodeElement<FadeModifierNode.Companion.Node>() {
override fun create(): Node = Node(enable)
override fun update(node: Node) {
node.enable = enable
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FadeModifierNode) return false
return enable == other.enable
}
override fun hashCode(): Int = enable.hashCode()
companion object {
class Node(
var enable: Boolean
) : Modifier.Node(), DrawModifierNode {
private var alpha = 0f
private val animatable = Animatable(initialValue = 1f)
override fun onAttach() {
coroutineScope.launch {
animatable.animateTo(if (enable) 0.5f else 1f)
}
}
override suspend fun onUpdate() {
animatable.animateTo(if (enable) 0.5f else 1f)
}
override fun ContentDrawScope.draw() {
alpha = animatable.value
drawContent()
drawRect(
color = Color.Black.copy(alpha = 1f - alpha),
blendMode = BlendMode.DstIn
)
}
}
}
}
// 扩展函数,使用更方便
fun Modifier.fadeNode(enable: Boolean): Modifier = this.then(FadeModifierNode(enable))