Widget, Element, RenderObject

Plumix ports Flutter's three-tree architecture to .NET rather than approximating it. This page explains what each tree is responsible for, and why the split matters when you write or port a control.

Three trees, three jobs

Most UI frameworks have one tree of long-lived, mutable control objects. Flutter — and therefore Plumix — splits that responsibility three ways, which is what makes rebuilding the entire UI on every state change affordable.

Widget

Immutable configuration

A plain, throwaway description of what the UI should look like. Rebuilt constantly; never holds mutable state.

Element

Mutable instance, owns lifecycle

Long-lived. Holds the position in the tree, the BuildContext, and any State. Decides whether an incoming widget updates it in place or replaces it.

RenderObject

Layout, paint, hit-test

Does the actual geometric work. Receives constraints, reports a size, paints into the layer tree, and answers hit-tests.

Widgets are cheap descriptions

Because a widget is immutable and holds no lifecycle, constructing one costs almost nothing. You are allowed to rebuild large subtrees on every frame; the framework diffs the result against the element tree and only touches what actually changed.

Badge.cs
// A widget is a description, not an instance. Creating one is cheap —
// the framework decides whether it needs a new element behind it.
public sealed class Badge : StatelessWidget
{
    public Badge(string label) => Label = label;

    public string Label { get; }

    public override Widget Build(BuildContext context) =>
        new Container(
            padding: new Thickness(8, 4),
            child: new Text(Label)
        );
}

Update or recreate

Each widget produces an element via CreateElement(). When a parent rebuilds, the existing element compares the new widget against the old one and takes one of two paths:

  • Update in place — the runtime type and Key match. The element keeps its identity, its State and its render object, and only the changed properties propagate downward.
  • Recreate — the type or key differs. The old element is unmounted, its state disposed, and a fresh subtree is built.

This is why keys matter when reordering a list: without them, position alone decides matching, and state follows the slot rather than the item.

Where Avalonia fits

Avalonia is used as infrastructure, not as a UI layer. It supplies the window, the platform lifecycle, raw input events and a GPU-backed drawing surface. Once a frame begins, the measure, arrange and paint decisions are made entirely by Plumix render objects — so a control ported from Flutter lays out the way its Flutter original does, not the way an equivalent Avalonia control would.

Continue with the layout protocol to see how constraints and sizes flow through that render tree, or stateful widgets for where mutable data lives.