Overview

The Sky render tree is a low-level layout and painting system based on a
retained tree of objects that inherit from RenderObject. Most
developers using Sky will not need to interact directly with the rendering tree.
Instead, most developers should use Sky widgets, which
are built using the render tree.


Flutter

Flutter is a new way to build high-performance, cross-platform mobile apps.
Flutter is optimized for today’s, and tomorrow’s, mobile devices. We are
focused on low-latency input and high frame rates on Android and iOS.

Base Model

The base class for every node in the render tree is
RenderObject, which defines the base layout model. The base
layout mode is extremely general and can accomodate a large number of more
concrete layout models that can co-exist in the same tree. For example, the base
model does not commit to a fixed number of dimensions or even a cartesian
coordinate system. In this way, a single render tree can contain render objects
operating in three-dimensional space together with other render objects
operating in two-dimensional space, e.g., on the face of a cube in the three-
dimensional space. Moreover, the two-dimensional layout might be partially
computed in cartesian coordinates and partially computed in polar coordinates.
These distinct models can interact during layout, for example determining the
size of the cube by the height of a block of text on the cube’s face.

Not entirely free-wheeling, the base model does impose some structure on the
render tree:

The base model also provides two mixins for common child models:

Subclasses of RenderObject are not required to use either of these child
models and are free to invent novel child models for their specific use cases.

Parent Data

TODO(ianh): Describe the parent data concept.

The setupParentData() method is automatically called for each child
when the child’s parent is changed. However, if you need to
preinitialise the parentData member to set its values before you add
a node to its parent, you can preemptively call that future parent’s
setupParentData() method with the future child as the argument.

TODO(ianh): Discuss putting per-child configuration information for
the parent on the child’s parentData.

If you change a child’s parentData dynamically, you must also call
markNeedsLayout() on the parent, otherwise the new information will
not take effect until something else triggers a layout.

Box Model

Dimensions

All dimensions are expressed as logical pixel units. Font sizes are
also in logical pixel units. Logical pixel units are approximately
96dpi, but the precise value varies based on the hardware, in such a
way as to optimise for performance and rendering quality while keeping
interfaces roughly the same size across devices regardless of the
hardware pixel density.

Logical pixel units are automatically converted to device (hardware)
pixels when painting by applying an appropriate scale factor.

TODO(ianh): Define how you actually get the device pixel ratio if you
need it, and document best practices around that.

EdgeDims

BoxConstraints

Bespoke Models

Using the provided subclasses

render_box.dart

RenderConstrainedBox

RenderShrinkWrapWidth

RenderOpacity

RenderColorFilter

RenderClipRect

RenderClipOval

RenderPadding

RenderPositionedBox

RenderImage

RenderDecoratedBox

RenderTransform

RenderSizeObserver

RenderCustomPaint

RenderBlock (render_block.dart)

RenderFlex (render_flex.dart)

RenderParagraph (render_paragraph.dart)

RenderStack (render_stack.dart)

Writing new subclasses

The RenderObject contract

If you want to define a RenderObject that uses a new coordinate
system, then you should inherit straight from RenderObject. Examples
of doing this can be found in RenderBox, which deals in
rectangles in cartesian space, and in the sector_layout.dart
example
, which
implements a toy model based on polar coordinates. The RenderView
class, which is used internally to adapt from the host system to this
rendering framework, is another example.

A subclass of RenderObject must fulfill the following contract:

The ParentData contract

Using RenderObjectWithChildMixin

Using ContainerRenderObjectMixin (and ContainerParentDataMixin)

This mixin can be used for classes that have a child list, to manage
the list. It implements the list using linked list pointers in the
parentData structure.

TODO(ianh): Document this mixin.

Subclasses must follow the following contract, in addition to the
contracts of any other classes they subclass:

TODO(ianh): Document how to walk the children.

The RenderBox contract

A RenderBox subclass is required to implement the following contract:

1
2
3
4
5
6
7
  class FooParentData extends BoxParentData { ... }

  // In RenderFoo
  void setupParentData(RenderObject child) {
    if (child.parentData is! FooParentData)
      child.parentData = new FooParentData();
  }

** It uses as input a set of constraints, described by a
BoxConstraints object, and a set of zero or more children, as
determined by the class itself, and has as output a Size (which is
set on the object’s own size field), and positions for each child
(which are set on the children’s parentData.position field).

** The algorithm can decide the Size in one of two ways: either
exclusively based on the given constraints (i.e. it is effectively
sized entirely by its parent), or based on those constraints and
the dimensions of the children.

In the former case, the class must have a sizedByParent getter that
returns true, and it must have a performResize() method that uses
the object’s constraints member to size itself by setting the
size member. The size must be consistent, a given set of
constraints must always result in the same size.

In the latter case, it will inherit the default sizedByParent
getter that returns false, and it will size itself in the
performLayout() function described below.

The sizedByParent distinction is purely a performance
optimisation. It allows nodes that only set their size based on the
incoming constraints to skip that logic when they need to be
re-laid-out, and, more importantly, it allows the layout system to
treat the node as a layout boundary, which reduces the amount of
work that needs to happen when the node is marked as needing
layout.

Using RenderProxyBox

The Hit Testing contract

Performance rules of thumb

Useful debugging tools

This is a quick way to dump the entire render tree to the console every frame.
This can be quite useful in figuring out exactly what is going on when
working with the render tree.

1
2
3
4
5
6
import 'package:sky/animation.dart';
import 'package:sky/rendering.dart';

scheduler.addPersistentFrameCallback((_) {
  SkyBinding.instance.debugDumpRenderTree();
});

Next steps: