compose-modifier-and-layout-style
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCompose modifier and layout style
Compose Modifier与布局风格
Core principle
核心原则
A composable that emits layout is a leaf the parent places — the parent decides position, size, alignment, padding. The composable's job is structure (what's inside), not placement (where it goes). Three rules follow:
- Declare a parameter and apply it to the root, so the parent can actually do its job. Hardcoding
modifieron a composable's root takes that decision away from every future caller..fillMaxWidth() - Construct modifier chains as one fluent expression, not stepwise reassignments. Both compile to the same thing, but the chain reads as intent in one pass.
- Conditional rendering belongs where the condition applies. A layout call whose only content is one exists solely to hold the condition — push the
ifoutside instead.if
These travel together because the same composable usually triggers all three: you declare its parameters (rule 1), the caller constructs a chain to position it (rules 2), and the body has a conditional you might be tempted to wrap (rule 3).
输出布局的可组合函数是由父组件放置的叶子节点——父组件决定其位置、大小、对齐方式和内边距。可组合函数的职责是定义内部结构(包含内容),而非放置逻辑(位置)。由此衍生三条规则:
- 声明参数并应用到根节点,这样父组件才能履行其职责。在可组合函数的根节点硬编码
modifier会剥夺后续所有调用方的布局决策权。.fillMaxWidth() - 将modifier链构建为连贯的表达式,而非分步重新赋值。两种写法编译结果一致,但链式写法能一次性清晰传达意图。
- 条件渲染应放在条件生效的位置。若某个布局调用的唯一内容是单个语句,那么该布局仅用于承载条件——应将
if语句移到布局外部。if
这三条规则通常同时适用,因为同一个可组合函数往往会触发所有三种情况:你需要声明其参数(规则1),调用方会构建链来定位它(规则2),而函数体中可能存在你想包裹的条件语句(规则3)。
When to use this skill
何时使用此规范
- You're writing a that calls a layout (
@Composable fun,Box,Column,Row,LazyColumn,Text,Image,Surface,Card, anything fromLayout { … }orcompose.foundation.layout) and its signature has nocompose.material*parameter, or has one that isn't applied to the root, or has a hardcodedmodifier/.fillMaxWidth()on the root..padding(...) - You see followed by
var m = Modifier,m = m.padding(…), etc.m = m.background(…) - A argument has three or more chained calls on a single line.
modifier = … - A composable's body is — one conditional, nothing else.
Layout { if (cond) Content() }
- 你正在编写一个调用布局的(如
@Composable fun、Box、Column、Row、LazyColumn、Text、Image、Surface、Card,或Layout { … }、compose.foundation.layout中的任意组件),但其签名中没有compose.material*参数,或虽有参数但未应用到根节点,或根节点硬编码了modifier/.fillMaxWidth()。.padding(...) - 你看到后跟着
var m = Modifier、m = m.padding(…)等赋值语句。m = m.background(…) - 参数在单行上有三个或更多链式调用。
modifier = … - 可组合函数的函数体是——仅包含一个条件语句,无其他内容。
Layout { if (cond) Content() }
1. Declare a modifier
parameter
modifier1. 声明modifier
参数
modifierFor composables that emit layout, prefer a parameter after required parameters and before content/lambda parameters, with a default of . The name is exactly — not , not , not .
modifierModifiermodifiermodmwrapperModifierkotlin
// ❌ BAD — no modifier param; caller can't position, size, or constrain this
@Composable
fun HomeScreenHeader(title: String, subtitle: String) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(title, style = MaterialTheme.typography.headlineLarge)
Text(subtitle, style = MaterialTheme.typography.bodyMedium)
}
}kotlin
// ✅ GOOD — parent decides width and padding; the composable describes structure only
@Composable
fun HomeScreenHeader(
title: String,
subtitle: String,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(title, style = MaterialTheme.typography.headlineLarge)
Text(subtitle, style = MaterialTheme.typography.bodyMedium)
}
}The caller now writes once, at the home screen — the only place that knows the layout actually wants those.
HomeScreenHeader(title, subtitle, Modifier.fillMaxWidth().padding(horizontal = 16.dp))对于输出布局的可组合函数,建议在必填参数之后、内容/ lambda参数之前添加参数,默认值为。参数名称必须是——不能是、或。
modifierModifiermodifiermodmwrapperModifierkotlin
// ❌ 错误示例 — 无modifier参数;调用方无法定位、调整大小或约束此组件
@Composable
fun HomeScreenHeader(title: String, subtitle: String) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(title, style = MaterialTheme.typography.headlineLarge)
Text(subtitle, style = MaterialTheme.typography.bodyMedium)
}
}kotlin
// ✅ 正确示例 — 父组件决定宽度和内边距;可组合函数仅描述结构
@Composable
fun HomeScreenHeader(
title: String,
subtitle: String,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(title, style = MaterialTheme.typography.headlineLarge)
Text(subtitle, style = MaterialTheme.typography.bodyMedium)
}
}现在调用方可在首页一次性编写——只有首页才知道该布局确实需要这些属性。
HomeScreenHeader(title, subtitle, Modifier.fillMaxWidth().padding(horizontal = 16.dp))2. Apply the caller's modifier to the root, and apply it first
2. 将调用方提供的modifier应用到根节点,且优先应用
When the root layout already takes other arguments (alignment, arrangement, padding that's intrinsic to the composable), the caller-provided modifier still goes on the root layout's parameter — and the composable's local chain is appended after.
modifierkotlin
// ❌ BAD — modifier accepted but never applied
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(painter = rememberAsyncImagePainter(url), contentDescription = null)
}
// ❌ BAD — applied to a child, not the root; caller's size/position changes don't take
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Box {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = modifier,
)
}
}
// ❌ BAD — caller's modifier ends up last, so the composable's own size wins
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = Modifier
.clip(CircleShape)
.size(48.dp)
.then(modifier),
)
}kotlin
// ✅ GOOD — caller's modifier first, then the composable's intrinsic chain
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = modifier
.clip(CircleShape)
.size(48.dp),
)
}Order matters: in a modifier chain, the earlier segment is the outer wrapper. The caller's modifier should be the outermost so caller-provided or can override the composable's defaults rather than being overridden by them.
.size(...).padding(...)当根布局已包含其他参数(如对齐方式、排列方式、属于可组合函数固有属性的内边距)时,调用方提供的modifier仍应传入根布局的参数——且可组合函数自身的modifier链应追加在其后。
modifierkotlin
// ❌ 错误示例 — 接收了modifier但从未应用
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(painter = rememberAsyncImagePainter(url), contentDescription = null)
}
// ❌ 错误示例 — 应用到子节点而非根节点;调用方的大小/位置修改无效
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Box {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = modifier,
)
}
}
// ❌ 错误示例 — 调用方的modifier排在最后,导致可组合函数自身的大小设置优先级更高
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = Modifier
.clip(CircleShape)
.size(48.dp)
.then(modifier),
)
}kotlin
// ✅ 正确示例 — 调用方的modifier在前,然后是可组合函数的固有链
@Composable
fun Avatar(url: String, modifier: Modifier = Modifier) {
Image(
painter = rememberAsyncImagePainter(url),
contentDescription = null,
modifier = modifier
.clip(CircleShape)
.size(48.dp),
)
}顺序至关重要:在modifier链中,更早的片段是外层包装。调用方的modifier应处于最外层,这样调用方提供的或才能覆盖可组合函数的默认值,而非被默认值覆盖。
.size(...).padding(...)3. Don't hardcode layout decisions on the root
3. 不要在根节点硬编码布局决策
If the composable's root has , , , etc., the caller can't not have them. Those are layout choices the parent should own.
.fillMaxWidth().padding(horizontal = 16.dp).height(56.dp)kotlin
// ❌ BAD — every caller now fills max width whether they want to or not
@Composable
fun PrimaryButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(
onClick = onClick,
modifier = modifier.fillMaxWidth(), // ← hardcoded
) { Text(text) }
}
// ✅ GOOD — caller adds .fillMaxWidth() if (and only if) they want it
@Composable
fun PrimaryButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(onClick = onClick, modifier = modifier) { Text(text) }
}The carve-out is for modifiers that are part of the identity of the composable — what makes an an avatar (the and a default ), not where it sits on the screen. Test: can you imagine a caller wanting a version of this composable without that modifier? If yes, push it out. If no (an avatar without isn't an avatar), keep it — but put it after the caller's modifier in the chain (see §2).
Avatar.clip(CircleShape).size(48.dp)clip(CircleShape)如果可组合函数的根节点包含、、等设置,调用方无法取消这些设置。这些布局选择应由父组件掌控。
.fillMaxWidth().padding(horizontal = 16.dp).height(56.dp)kotlin
// ❌ 错误示例 — 所有调用方现在都必须填充最大宽度,无论是否需要
@Composable
fun PrimaryButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(
onClick = onClick,
modifier = modifier.fillMaxWidth(), // ← 硬编码
) { Text(text) }
}
// ✅ 正确示例 — 调用方仅在需要时添加.fillMaxWidth()
@Composable
fun PrimaryButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
Button(onClick = onClick, modifier = modifier) { Text(text) }
}例外情况是属于可组合函数标识属性的modifier——即构成本质的属性(如和默认的),而非其在屏幕上的位置。测试方法:你能否想象调用方需要一个没有该modifier的可组合函数版本?如果是,将其移到外部;如果否(没有的头像就不是头像),则保留它——但要将其放在调用方modifier链的后面(参见第2节)。
Avatar.clip(CircleShape).size(48.dp)clip(CircleShape)4. Construct modifier chains as one fluent expression
4. 将modifier链构建为连贯的表达式
Recomposition re-runs the composable body — every modifier expression is re-evaluated. Reassigning step-by-step looks plausible but breaks the visual flow, invites further mutation, and produces nothing a chain doesn't.
var modifier =kotlin
// ❌ BAD — visual flow broken into reassignments; `var` invites more mutation
@Composable
fun Demo() {
var m = Modifier
m = m.padding(16.dp)
m = m.fillMaxSize()
Box(m) { }
}
// ❌ ALSO BAD — same shape, dressed up with .then()
@Composable
fun Demo() {
var m = Modifier
m = m.padding(16.dp)
m = m.then(Modifier.fillMaxSize())
Box(m) { }
}kotlin
// ✅ GOOD
@Composable
fun Demo() {
val m = Modifier
.padding(16.dp)
.fillMaxSize()
Box(m) { }
}valvarvar重组会重新运行可组合函数体——每个modifier表达式都会被重新计算。分步重新赋值看似合理,但会破坏视觉流程,引发更多突变,且无法产生链式写法之外的任何效果。
var modifier =kotlin
// ❌ 错误示例 — 视觉流程被拆分为多次赋值;`var`容易引发更多突变
@Composable
fun Demo() {
var m = Modifier
m = m.padding(16.dp)
m = m.fillMaxSize()
Box(m) { }
}
// ❌ 同样错误 — 用.then()包装,但本质相同
@Composable
fun Demo() {
var m = Modifier
m = m.padding(16.dp)
m = m.then(Modifier.fillMaxSize())
Box(m) { }
}kotlin
// ✅ 正确示例
@Composable
fun Demo() {
val m = Modifier
.padding(16.dp)
.fillMaxSize()
Box(m) { }
}使用而非:链构建完成后,不应重新绑定它。分步赋值的写法让看起来是必需的,但链式写法不需要它。
valvarvarInline at the call site is fine for short chains
短链可直接在调用处内联
For one or two calls, build the modifier inline. The "extract to a " rule only earns its keep when the chain is long enough to be worth naming, or when the same chain repeats.
valkotlin
// ✅ GOOD — short chain inline
Box(modifier = Modifier.fillMaxWidth()) { … }
Box(modifier = Modifier.padding(8.dp).background(Color.Red)) { … }对于1-2个调用,可直接在调用处构建modifier。只有当链足够长值得命名,或同一链重复出现时,才需要将其提取为。
valkotlin
// ✅ 正确示例 — 短链内联
Box(modifier = Modifier.fillMaxWidth()) { … }
Box(modifier = Modifier.padding(8.dp).background(Color.Red)) { … }Conditional segments stay on the chain
条件片段保留在链中
A common reason to reach for is "the modifier depends on a condition." It doesn't — splice the condition inline:
varkotlin
// ✅ GOOD — conditional inside the chain, still one expression
Box(
modifier = Modifier
.fillMaxWidth()
.then(if (selected) Modifier.background(Color.Red) else Modifier),
)Modifier.then使用的常见原因是“modifier依赖于条件”。其实无需如此——可将条件内联到链中:
varkotlin
// ✅ 正确示例 — 条件在链内,仍为单个表达式
Box(
modifier = Modifier
.fillMaxWidth()
.then(if (selected) Modifier.background(Color.Red) else Modifier),
)Modifier.then5. Multiline formatting at the call site
5. 调用处的多行格式化
When a argument's chain has three or more calls, format multiline with one call per line. Indent the chain so the dotted calls align beneath the value.
modifierkotlin
// ❌ BAD — three+ calls on one line; hard to scan
Box(
modifier = modifier.fillMaxSize().padding(16.dp).weight(1f),
)
// ✅ GOOD
Box(
modifier = modifier
.fillMaxSize()
.padding(16.dp)
.weight(1f),
)One or two calls stay on a single line — the threshold is the call count, not the character count. If a single call has very long arguments, that's a different problem (extract a , or shorten the arguments).
valThis applies only to a parameter named . Other fluent-style arguments aren't covered here.
modifier当参数的链包含三个或更多调用时,应格式化为多行,每行一个调用。缩进链,使带点的调用对齐在值的下方。
modifierkotlin
// ❌ 错误示例 — 三个及以上调用在一行;难以阅读
Box(
modifier = modifier.fillMaxSize().padding(16.dp).weight(1f),
)
// ✅ 正确示例
Box(
modifier = modifier
.fillMaxSize()
.padding(16.dp)
.weight(1f),
)1-2个调用应保留在单行——判断标准是调用次数,而非字符数。如果单个调用的参数很长,那是另一个问题(提取为,或缩短参数)。
val此规则仅适用于名为的参数。其他流畅风格的参数不在此范围内。
modifier6. Hoist single conditionals out of the layout
6. 将单个条件语句移到布局外部
When a layout's only content is one , the layout exists solely to "hold" the conditional. Move the outside — the layout will only exist when it has something to show.
ififkotlin
// ❌ BAD — Column always emitted; only its inner content is conditional
@Composable
fun A() {
Column {
if (showHeader) {
Text("Title")
Text("Subtitle")
}
}
}
// ✅ GOOD — Column only exists when it has content
@Composable
fun A() {
if (showHeader) {
Column {
Text("Title")
Text("Subtitle")
}
}
}The benefit isn't a performance win — the runtime handles both fine — it's that the second form reads as "header section, conditionally." The first reads as "always-on column that may or may not have content."
当布局的唯一内容是单个语句时,该布局仅用于“承载”条件语句。将语句移到外部——只有当有内容要显示时,布局才会存在。
ififkotlin
// ❌ 错误示例 — Column始终被输出;只有内部内容是条件性的
@Composable
fun A() {
Column {
if (showHeader) {
Text("Title")
Text("Subtitle")
}
}
}
// ✅ 正确示例 — 仅当有内容时Column才存在
@Composable
fun A() {
if (showHeader) {
Column {
Text("Title")
Text("Subtitle")
}
}
}好处并非性能提升——运行时能很好地处理两种写法——而是第二种写法的语义更清晰:“标题区域,按需显示”。第一种写法则是“始终存在的列,可能有内容也可能没有”。
The carve-outs (and why)
例外情况(及原因)
-
Layout carries visual semantics that aren't conditional. When the layout call passes,
modifier,contentAlignment, orhorizontalArrangement, those arguments describe the container, not the content. Hoisting the conditional either loses those (the container collapses with the content) or duplicates them into both branches. Leave it.verticalAlignmentkotlin// ✅ KEEP AS-IS — modifier on the container is doing visible work @Composable fun A(modifier: Modifier = Modifier) { Box(modifier = modifier) { if (something) { Text("Bleh1") Text("Bleh2") } } } -
There are siblings to the. The layout has other content; the
ifis just one piece. Hoisting either pulls the siblings out (changing the layout) or leaves a different shape behind. Leave it.if -
with both branches contributing composables. Both branches do work; nothing to hoist; the layout is the shared container.
if … else …kotlin// ✅ KEEP AS-IS — both branches contribute to the layout Box { if (something) Text("Hint") else innerTextField() }
-
布局承载非条件性的视觉语义。当布局调用传递、
modifier、contentAlignment或horizontalArrangement时,这些参数描述的是容器而非内容。将条件语句移到外部要么会丢失这些属性(容器随内容一起消失),要么会在两个分支中重复这些属性。保留原写法。verticalAlignmentkotlin// ✅ 保持原样 — 容器上的modifier在发挥可见作用 @Composable fun A(modifier: Modifier = Modifier) { Box(modifier = modifier) { if (something) { Text("Bleh1") Text("Bleh2") } } } -
语句有同级内容。布局有其他内容;
if只是其中一部分。将条件语句移到外部要么会把同级内容移出(改变布局),要么会留下不同的结构。保留原写法。if -
的两个分支都提供可组合函数。两个分支都有作用;没有可移动的内容;布局是共享容器。
if … else …kotlin// ✅ 保持原样 — 两个分支都对布局有贡献 Box { if (something) Text("Hint") else innerTextField() }
Quick reference
快速参考
| Symptom | Diagnosis | Fix |
|---|---|---|
| No | Add |
| Param ignored (§2) | Apply to root layout's |
| Wrong target (§2) | Move to the outermost layout's |
| Caller's modifier last (§2) | Reorder: |
| Layout hardcoded (§3) | Remove the hardcoded calls; let callers add them |
Sibling composables in the file don't have | Spreading anti-pattern | Fix this one; fix siblings opportunistically |
| Wrong name (§1) | Rename to exactly |
| Stepwise modifier construction (§4) | One fluent chain on a |
| Same shape via | Collapse |
| Modifier branch needs a condition | Reaching for | |
| Long chain not formatted (§5) | One call per line, indented under the value |
| Hoist (§6) | Move the |
| Layout carries semantics — leave (§6 carve-out) | Keep as-is |
| Both branches contribute — leave (§6 carve-out) | Keep as-is |
| 症状 | 诊断 | 修复方案 |
|---|---|---|
| 无 | 添加 |
声明了 | 参数被忽略(第2节) | 应用到根布局的 |
| 目标错误(第2节) | 移到最外层布局的 |
| 调用方的modifier排在最后(第2节) | 重新排序: |
通用组件的 | 布局被硬编码(第3节) | 移除硬编码调用;让调用方添加 |
文件中的同级可组合函数也没有 | 反模式扩散 | 修复当前组件;适时修复同级组件 |
| 参数名称错误(第1节) | 重命名为 |
| 分步构建modifier(第4节) | 用 |
| 用 | 将 |
| Modifier分支需要条件判断 | 试图使用 | 在链中使用 |
| 长链未格式化(第5节) | 每行一个调用,缩进对齐 |
| 应移到外部(第6节) | 将 |
| 布局承载语义——保留(第6节例外) | 保持原样 |
| 两个分支都有贡献——保留(第6节例外) | 保持原样 |
When NOT to apply
何时不适用
- Composables that don't emit layout. A or a
@Composable fun computeColor(): Coloraccessor doesn't emit a layout node. No@Composable @ReadOnlyComposableparameter needed (and amodifiercouldn't accept one — see@ReadOnlyComposable).compose-state-authoring - functions. Previews are throwaway entry points; the framework calls them with no caller. A
@Previewparameter would be unused dead weight.modifier - Test-only composables inside sources whose only caller is
*Test. Same reasoning as previews.composeTestRule.setContent { … } - Internal layout primitives that take a as their first required parameter (very rare; framework-level). The rule is "first optional param"; some private utilities legitimately have
modifierupfront as required.modifier - Modifier assembled imperatively from animation state. A modifier built by appending values from or other procedural sources may legitimately need intermediate variables. The chain isn't the goal; readability is. If the chain becomes a worse expression, write the imperative form.
Animatable - Slot APIs that store modifiers in a data class or builder (rare; usually framework-level code). The fluent-chain idea is about user-site construction.
- Test composables pinning specific recomposition shapes — usually fine either way; don't refactor test composables purely for style.
The declaration-side rules (§1–§3) should not be skipped merely because "this composable is internal", "only used in one place", "I'd rather not have the extra parameter on the signature", or "we know all the callers already". Those are exactly the rationalisations that produce composables that become single-use the day someone wants to call them twice.
- 不输出布局的可组合函数。或
@Composable fun computeColor(): Color访问器不输出布局节点。无需@Composable @ReadOnlyComposable参数(且modifier无法接受该参数——参见@ReadOnlyComposable)。compose-state-authoring - 函数。预览是一次性入口点;框架调用它们时没有调用方。
@Preview参数会成为未使用的无效代码。modifier - 源文件中的测试专用可组合函数,其唯一调用方是
*Test。与预览的理由相同。composeTestRule.setContent { … } - 将作为第一个必填参数的内部布局原语(非常罕见;框架级)。规则是“第一个可选参数”;一些私有工具合理地将
modifier作为必填参数放在前面。modifier - 从动画状态命令式组装的Modifier。由或其他过程化源追加值构建的modifier可能确实需要中间变量。链式写法并非目标;可读性才是。如果链式写法的可读性更差,就使用命令式写法。
Animatable - 在数据类或构建器中存储modifier的插槽API(罕见;通常是框架级代码)。流畅链的理念适用于用户端构建。
- 固定特定重组结构的测试可组合函数——通常两种写法都可行;不要仅为了风格而重构测试可组合函数。
声明端规则(第1-3节)不应仅仅因为“此可组合函数是内部的”、“仅在一处使用”、“我不想在签名中添加额外参数”或“我们已经知道所有调用方”而跳过。这些正是导致可组合函数在有人想第二次调用时就变成一次性组件的理由。
Red flags during review
评审中的危险信号
| Thought | Reality |
|---|---|
"This composable is internal-only — adding | The parameter is eight characters and a default. It's not over-engineering; it's the convention. Skipping it is the over-engineering — it's a custom decision against the grain of every Compose API. |
| "It's only used in one place, so I know the layout requirements" | "Only used in one place" describes today. The cost of the parameter is paid once; the cost of refactoring callers when the second use site appears is paid per caller. |
"The sibling composables in this file don't have | Spreading an anti-pattern isn't matching style. Fix this one. Fix the siblings opportunistically. |
"The parent always wants | Then the parent passes |
| "I'll add it when someone needs it" | You're someone. You need it now (for the convention). The next caller won't add it either — they'll work around its absence. |
| "It's a tiny composable — the modifier param is noise" | The param is eight characters at the declaration and zero characters at any call site that doesn't need it. The "noise" is imagined. |
"I added | Then the not-home-screen caller can't unset it. Move the |
"I need | A conditional segment is |
| "Three lines is too few to make multiline" | Three chained calls is the threshold. Below three, one line. At or above three, multiline. |
| "The Column adds nothing but I'll keep it for symmetry" | Then hoist the conditional and keep the Column inside the consequent — symmetry preserved, no always-on container. |
"I'll put the | "Already exists" is the bug. The layout shouldn't exist when the condition is false. |
| 想法 | 实际情况 |
|---|---|
“此可组合函数是内部专用的——添加 | 该参数只有8个字符和一个默认值。这不是过度设计;这是约定。跳过它才是过度设计——这是违背所有Compose API惯例的自定义决策。 |
| “它仅在一处使用,所以我知道布局要求” | “仅在一处使用”描述的是现在。添加参数的成本是一次性的;当出现第二个使用场景时,重构调用方的成本是每个调用方都要付出的。 |
“此文件中的同级可组合函数也没有 | 扩散反模式不是保持风格一致。修复当前组件。适时修复同级组件。 |
“父组件总是希望这里使用 | 那么父组件应传递 |
| “等有人需要时我再添加” | 你就是需要它的人。你现在就需要它(遵循约定)。下一个调用方也不会添加它——他们会绕过这个缺陷。 |
| “这是一个很小的可组合函数——modifier参数是冗余的” | 该参数在声明处只有8个字符,在不需要它的调用处是0个字符。所谓的“冗余”是想象出来的。 |
“我添加了 | 那么非首页的调用方无法取消它。将 |
“我需要用 | 条件片段可以写成 |
| “三行太少,没必要写成多行” | 三个链式调用就是阈值。少于三个,单行;等于或多于三个,多行。 |
| “Column没有实际作用,但我为了对称保留它” | 那么将条件语句移到外部,并将Column保留在结果分支中——对称性得以保留,没有始终存在的容器。 |
“我把 | “已经存在”就是问题所在。当条件为false时,布局不应存在。 |
Related
相关内容
- — the other half of declaring a reusable composable's public API: take
compose-slot-api-patternslots for variable content. A reusable component takes both a@Composable () -> Unitparameter and slots — caller owns placement and what to place.modifier
- — 声明可复用可组合函数公共API的另一半:接受
compose-slot-api-pattern插槽用于可变内容。可复用组件应同时接受@Composable () -> Unit参数和插槽——调用方掌控放置位置和放置内容。modifier