Mobile Development 14 min read

Designing a New Rendering Architecture for Flutter Dynamic Templates

We redesigned Flutter’s dynamic‑template rendering by replacing the widget‑centric approach with a three‑layer architecture inspired by Android’s MeasureSpec model, implementing DXRenderBox to handle sizedByParent, performResize, and performLayout, solving two‑measure layout issues and achieving faster, more consistent rendering across platforms.

Xianyu Technology
Xianyu Technology
Xianyu Technology
Designing a New Rendering Architecture for Flutter Dynamic Templates

Our team explored using the DinamicX DSL to render Flutter UI dynamically. After solving performance problems, we faced a new challenge: how to improve rendering consistency between Flutter and native platforms without sacrificing speed.

Idea

The initial widget‑centric architecture converted DSL nodes to Widgets. As template complexity grew, several Bad Cases appeared because:

Stack was used to emulate FrameLayout and Column/Row to emulate LinearLayout; although they look similar, their internal implementations differ greatly.

Custom Widgets only partially aligned with the DSL layout philosophy, leaving many edge cases unresolved.

To solve these issues we redesigned the rendering architecture based on Android’s MeasureSpec model.

New Rendering Architecture

Decoration layer – supports background, border, corner radius, touch events, etc.

Render layer – expresses the layout rules and size of each DSL node.

Content layer – displays actual content; for layout widgets it is the children, for leaf widgets it uses RenderParagraph , RenderImage , etc.

The core class is DXRenderBox , a subclass of RenderBox . It implements three essential methods:

sizedByParent – determines whether the size is dictated solely by the parent.

performResize – calculates size when sizedByParent is true.

performLayout – lays out children and computes the final size when sizedByParent is false.

Key Code Snippets

abstract class RenderObject {
  Constraints get constraints => _constraints;
  Constraints _constraints;
  bool get sizedByParent => false;
  void layout(Constraints constraints, {bool parentUsesSize = false}) {
    // calculate relayoutBoundary
    // ...
    if (sizedByParent) {
      performResize();
    } else {
      performLayout();
    }
    // ...
  }
}

In DXRenderBox we override sizedByParent to reflect the DSL’s MeasureSpec mode:

bool get sizedByParent =>
    nodeData.widthMeasureMode == DXMeasureMode.DX_EXACTLY &&
    nodeData.heightMeasureMode == DXMeasureMode.DX_EXACTLY;

The performResize implementation extracts explicit width/height or falls back to the parent constraints:

void performResize() {
  double width = nodeData.width ?? constraints.maxWidth;
  double height = nodeData.height ?? constraints.maxHeight;
  size = constraints.constrain(Size(width, height));
}

For non‑layout widgets we use DXSingleChildLayoutRender :

void performLayout() {
  BoxConstraints childBoxConstraints = computeChildBoxConstraints();
  if (sizedByParent) {
    child.layout(childBoxConstraints);
  } else {
    child.layout(childBoxConstraints, parentUsesSize: true);
    size = defaultComputeSize(child.size);
  }
}

Layout widgets such as DXFrameLayoutRender first layout all children, compute the maximum width/height, then determine their own size and finally calculate each child’s offset based on gravity‑to‑alignment conversion.

One tricky scenario is the “two‑measure” problem when a parent’s width is match_content and its children are match_parent . Android measures children twice; Flutter’s default layout order cannot satisfy this. We solve it by leveraging intrinsic methods ( getMinIntrinsicWidth , getMaxIntrinsicWidth , getMinIntrinsicHeight , getMaxIntrinsicHeight ) to obtain children’s preferred dimensions before the parent size is fixed.

@override
double getMaxIntrinsicWidth(double height) {
  if (nodeData.width != null) return nodeData.width;
  if (child != null) return child.getMaxIntrinsicWidth(height);
  return 0.0;
}

Using these intrinsic queries we can first determine the maximum width required by all children, set the parent’s width accordingly, and then perform a second layout pass with the exact width, achieving the same effect as Android’s two‑measure flow.

Performance tests show that the new architecture reduces rendering time and improves FPS for dynamic templates, while also eliminating the previously observed Bad Cases.

Future Outlook

After the architecture upgrade we have fully resolved the earlier Bad Cases and further boosted rendering performance, making Flutter dynamic template rendering practical. We will continue to refine the solution and expand its capabilities to empower business scenarios.

flutterPerformancerenderingdsllayoutmobile
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.