Lesson 5
From a node operation to LayoutNode
Connect compiler-facing node operations to the Compose UI tree without equating every composable with a node.
Status: The distinction between group calls and node calls is Durable .
ComposeUiNodeandLayoutNodedetails are Version-specific at the pinned revision.
Outcome
You should be able to trace one target-node insertion and explain why a wrapper composable can run without inserting anything.
Group work is not node work
The Composer protocol has both group operations and node operations. A group records logical composition structure. A node operation asks the applier-owned tree to create, retain, or update a target object.
This distinction matters because many useful composables do not emit nodes. A wrapper may provide a scope. A state read may register an observation. A control-flow branch may change groups. None of those facts alone means that a new UI object exists.
flowchart LR
NodeCall[ComposeNode call] --> Record[Recorded node operation]
Record --> Applier[UI applier]
Applier --> Layout[LayoutNode]
Layout --> Later[Measure and draw later]The final box is deliberately brief. Measurement and drawing are downstream UI work, not Chapter 1’s subject.
The Compose UI bridge
The generic runtime only knows an applier and a node type. Compose UI supplies ComposeUiNode. Its constructor points to LayoutNode.Constructor, and its update properties include values such as the modifier, measure policy, density, and composition locals.
That gives this concrete path:
- Compiler-generated code invokes the runtime node protocol.
- Composition records a create or update operation.
applyChangessends the operation to the Compose UI applier.- The applier inserts or updates a
LayoutNode. - Later UI phases decide whether measurement, placement, or drawing work is needed.
Steps 1–3 are runtime protocol boundaries. Step 4 is the Compose UI bridge. Step 5 belongs to downstream UI internals.
Insert, retain, update
On first composition, a node-producing call can create and insert a node. On a later execution, the runtime can retain that node and apply a property update instead. The node object need not be recreated just because the composable body was considered again.
The pinned LayoutNode.kt exposes child insertion, removal, and movement. Those methods are target-tree operations. They are not evidence that the slot storage and node tree are the same structure.
A logging-applier exercise
Use a small LogNode tree and an AbstractApplier<LogNode>. Log these methods:
insertTopDown;remove;move; and- the property block used by a node update.
Compose one node with label first, apply changes, then change only its label and apply again. The useful observation is one insertion and two property applications, not a second insertion. A real test should assert tree state and operation counts. Log text is only a debugging aid.
Check yourself
A composable calls Marker(), which reads state but never calls ComposeNode. Which operations can still change? Which operation should you not assume?
Source notes
| Claim | Direct evidence | Status |
|---|---|---|
| The runtime exposes separate group and node operations | Composer.kt, startNode, createNode, and useNode | Durable
Version-specific |
Compose UI supplies the ComposeUiNode bridge | ComposeUiNode.kt | Version-specific |
LayoutNode owns concrete child operations | LayoutNode.kt, insertAt, removeAt, and move | Version-specific |
Freshness
Refresh this lesson when Composer node operations, ComposeUiNode, or LayoutNode child operations move. Keep measurement and drawing as downstream context unless the chapter boundary changes.
Finished this lesson?
Your progress stays only in this browser.