ComposedModifier
What is it?这是啥?
在 Compose 宇宙🌌里面,有很多的 Modifier,大致可以分为 3 类:
- 没有实际作用的 Modifier 单例对象,纯粹作为链式调用的起点;
- 对可组合内容起真正修饰作用的 Element 接口实现类,如
SizeElement
用于设置尺寸、PaddingElement
用于设置边距...; - 用于合并 Modifier 的 CombinedModifier。
在众多 Element 实现类中,有一个特殊的 ComposedModifier,它自身并没有修饰可组合内容的能力。查看 ComposedModifier 的源码,很简单,只声明了 1 个成员变量 factory
,而且没有任何其他成员方法。
// ComposedModifier.kt
// 并没有省略其他代码,ComposedModifier 类就是这么简单
private open class ComposedModifier(
inspectorInfo: InspectorInfo.() -> Unit,
val factory: @Composable Modifier.() -> Modifier
) : Modifier.Element, InspectorValueInfo(inspectorInfo)
构造函数里的 inspectorInfo
是用于调试的,我们不用关心。
成员变量 factory
是一个函数类型,拥有 @Composable 上下文,接收者是 Modifier,返回值也是 Modifier。我们可以简单地理解为:factory
就是一段能生成 Modifier 实例的代码。
- ComposedModifier 是一个 Modifier(间接实现了 Modifier 接口);
- ComposedModifier 自身不具备修饰可组合内容的能力;
- ComposedModifier 的内部保存了一段能生成 Modifier 实例的代码。
基于这几点,我们可以认为:ComposedModifier 是一个可以延迟初始化 Modifier 的 Modifier。可以延迟初始化 Modifier 的 Modifier,再读一遍,还是很懵。既然懵,那干脆先别管有什么用,就先看它是怎么用吧,说不定看到用法就理解了作用呢。
How to use?怎么用?
使用 ComposedModifier 的方式很简单,调用 Modifier.composed()
函数,传入一个函数参数,就是 ComposedModifier 里面的 factory
。
// ComposedModifier.kt
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
Modifier.composed()
背后会使用 then()
将 ComposedModifier 添加到 Modifier 链中。
那么我们在创建 Modifier 的时候,就可以这么写:
val composedModifier = Modifier.composed { size(100.dp) }
val normalModifier = Modifier.size(100.dp)
和普通创建 modifier 的方式相比,区别在于:
- 执行
Modifier.size(100.dp)
的过程中会立刻创建出一个SizeElement
; - 执行
Modifier.composed { size(100.dp) }
过程中并不会立刻创建出一个 SizeElement,而是创建出一个 ComposedModifier,这个 ComposedModifier 内部保存了一段能生成 SizeElement 的代码,也就是factory
。只有当 ComposedModifier 被使用的时候,才会执行factory
生成 SizeElement。
被使用的时候才会执行 factory
生成 SizeElement。被使用的时候?所谓何时?
Box(modifier = composedModifier)
我们把 modifier 作为参数传给可组合内容,在可组合内容发生组合(Composition)的时候,modifier 就会被使用。
我们可以点进 Box 的源码,跟踪参数 modifier 在哪个地方被使用。

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
}
)
}
...
return result
}
在 materialize()
函数中,首先会判断 modifier 链是否不包含 ComposedModifier:如果整个 modifier 链都不包含 ComposedModifier,那么就直接返回 modifier 链,不做任何处理。
if (modifier.all { it !is ComposedModifier }) {
return modifier
}
如果 modifier 链包含 ComposedModifier,那么就使用 Modifier.foldIn()
遍历 modifier 链,将其中的 ComposedModifier 替换成它的 factory
生成的 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
}
)
}
原来 ComposedModifier 是一个延迟初始化 Modifier 的 Modifier 是这么个意思啊。在发生组合(Conpisition)的时候,才会执行 factory()
生成真正起作用的 Modifier。
用法明白了,作用也算理解了。but...应该在什么场景下使用 ComposedModifier 呢?
val modifier1 = Modifier.size(100.dp)
val modifier2 = Modifier.composed { size(100.dp) }
这两种写法好像对日常使用完全无差别啊... 什么时候初始化 Modifier,我又不关心,反正最终得到的 Modifier 链是一样的。
When to use? 什么时候使用?
的确,在日常开发中,我们并不关心 Modifier/Element 的初始化时机,前面一直在说 “ComposedModifier 是一个可以延迟初始化 Modifier 的 Modifier”,但 Compose 官方创建 ComposedModifier 的初衷,根本不是为了延迟初始化 Modifier。
ComposedModifier 的主要价值是用于实现有“状态的 Modifier”。
我们看看 Modifier.composed()
方法的注释:
Declare a just-in-time composition of a Modifier that will be composed for each element it modifies. 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.
...
materialize must be called to create instance-specific modifiers if you are directly applying a Modifier to an element tree node.
大概意思就是:可用于实现有“状态的 Modifier”,能为每个 Element(Modifier) 提供特定于实例的状态,从而使同一个 Modifier 实例可安全地重复用于多个元素,同时保持特定于元素的状态。
有状态的 Modifier?什么东西?val modofier = Modifier.size(100.dp)
所创建出来的 Modifier 没有状态吗?
别急,谈起“有状态 Stateful”和“无状态 Stateless”,还得从 Composable 函数说起,观察下面两个 @Composable 函数:
@Composable
fun StatefulCounterText() {
var count by remember { mutableIntStateOf(0) }
Text(
text = count.toString(),
modifier = Modifier.clickable { count++ }
)
}
@Composable
fun StatelessCounterText(count: Int, onClick: () -> Unit) {
Text(
text = count.toString(),
modifier = Modifier.clickable(onClick = onClick)
)
}
StatefulCounterText()
是无参函数,意味着它不依赖任何外部状态。由remember()
创建的 count 变量表示它的内部状态,这种状态会在重组时被保留,因此这是一个有状态的(Stateful)Composable 函数。StatelessCounterText(count: Int...)
将显示内容 count 作为参数由外部传入,这表明它依赖于外部状态,在其内部并不会管理任何状态,而是依赖于提供的参数来确定其行为,因此这被认为是一个无状态的(Stateless)Composable 函数。
话归正题,说回“无状态的 Modifier”和“有状态的 Modifier”。
val modifier = Modifier.size(100.dp)
// 相当于
val bigSize = 100.dp
val modifier = Modifier.size(bigSize)
修饰符 size(...)
的调用,需要我们显式传参,提供一个尺寸,所以这个 Modifier 的行为是依赖于外部状态 bigSize
的,因此这是一个无状态的 Modifier。
再来看一个例子,假设要用 Modifier.rotate()
来制作做无限循环的旋转动画,那么就必须要给 Modifier.rotate()
传递旋转的角度,角度就是无状态 modifier 的外部依赖
val infiniteRotationTransition = rememberInfiniteTransition()
val rotation by infiniteRotationTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000),
repeatMode = RepeatMode.Restart
)
)
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Box(
modifier = Modifier
.size(100.dp)
.rotate(rotation)
.background(purple)
)
}

因为所创建的 Modifier 是无状态的,行为依赖于外部状态 rotation
,如果需要在其他页面也想使用 modifier 来实现相同的旋转动画,那么就需要在其他页面也创建外部状态,然后再将外部状态传给 modifier......
val infiniteRotationTransition = rememberInfiniteTransition()
val rotation by infiniteRotationTransition.animateFloat(...)
val modifier = Modifier
...
.rotate(rotation)
这样还怎么复用啊,每用一次都要创建一遍外部状态... 有没有什么办法,可以将 Modifier 所依赖的状态封装到 Modifier 内部呢。“将状态封装到 Modifier 内部”,这不就是“有状态的 Modifier”吗?
ComposedModifier:我好像听到有人在叫我?
fun Modifier.rotating(): Modifier = composed {
val infiniteTransition = rememberInfiniteTransition()
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000),
repeatMode = RepeatMode.Restart
)
)
rotate(rotation)
}
利用 Modifier.composed()
我们将依赖的状态 rotation
挪到了 ComposedModifier 的内部,这样我们就创建出了一个有状态的 Modifier。现在这个 Modifier 不依赖于外部状态了,那在其他地方复用时就简单多了,直接链式调用 .rotating()
就可以了。
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Box(
modifier = Modifier
.size(100.dp)
.rotating()
.background(purple)
)
}
我们在不同的地方链式调用 .rotating()
得到 modifier 链,然后传递给可组合内容,当发生组合时,这条 modifier 链就会被使用,接着就会遍历 modifier 链,执行 ComposedModifier 里面的 factory 函数,为每个实例创建自己的依赖。
讲到这,回顾一下,ComposedModifier 是拿来干嘛的?就是用于在自定义 Modifier 时,创建“有状态的 Modifier”。
Not recommended for use 不推荐使用
因为性能等原因,目前官方已经不推荐我们使用 Modifier.composed()
了,想要了解更多为什么,可以看看这个 Youtube 视频:Compose Modifiers deep dive。
好家伙,废了这么多脑细胞,脑子痒痒的,好像刚明白一点,最后你告诉我不推荐使用了...

不推荐使用,那就是有别的办法呗,对于上面的例子,我们可以这么写,这也是目前官方的推荐写法:
@Composable
fun Modifier.rotating(): Modifier {
val infiniteTransition = rememberInfiniteTransition()
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(2000),
repeatMode = RepeatMode.Restart
)
)
return this then Modifier.rotate(rotation)
// 或者写成 return rotate(rotation)
}
这种写法相当于是把壳子 ComposedModifier 换成了 CombinedModifier,弃用了 ComposedModifier,也不存在什么延迟初始化 Modifier 了,对重组的性能会比较好。