Mobile Development 32 min read

Flutter Hybrid Development: Core Principles, Rendering Process, and Cross‑Platform Solutions

The article explains Flutter’s three‑layer architecture and rendering pipeline, compares web‑container, generic‑container, and self‑draw engine cross‑platform approaches, details unified and three‑side hybrid integration patterns with navigation scenarios, evaluates hybrid frameworks, and discusses compilation modes, artifact integration, and engineering workflow for hybrid projects.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Flutter Hybrid Development: Core Principles, Rendering Process, and Cross‑Platform Solutions

In the "Fan Live" Flutter hybrid development practice, we summarize a series of experiences. This article first explains Flutter's rendering principles, then compares three cross‑platform solutions, proceeds to the analysis of four Flutter hybrid development modes, and finally shares engineering explorations.

1. Flutter Core Principles

1.1 Flutter Architecture

Flutter adopts a three‑layer architecture:

Embedder : OS adaptation layer that sets up the rendering surface, threads, etc.

Engine : Implements the rendering engine, text layout, event handling, and the Dart runtime. It includes the Skia graphics library, the Dart VM, and text services.

Framework : A UI SDK written in Dart, providing widget libraries, graphics drawing, gesture recognition, animation, and more.

1.2 Flutter Rendering Principle

The rendering pipeline can be summarized as follows: user interaction updates the Widget Tree , which builds an Element Tree . The diff result is synchronized to the RenderObject Tree , which performs layout, painting, compositing, and finally engine rendering.

1.3 Data Structures in the Rendering Process

Three trees and a layer are involved:

Widget Tree : Immutable description of UI components.

Element Tree : Mutable instances created by createElement , bridging Widgets to RenderObjects.

RenderObject Tree : Actual objects that perform layout and painting.

Key code snippets are shown below.

@immutable
abstract
class Widget
extends
DiagnosticableTree {
  const Widget({this.key});
  final Key key;
  @protected
  @factory
  Element createElement();
  // ... other members omitted
}

The Element holds both a Widget and a RenderObject , but the actual drawing is performed by the RenderObject .

RenderObject get renderObject {
  RenderObject result;
  void visit(Element element) {
    assert(result == null);
    if (element is RenderObjectElement)
      result = element.renderObject;
    else
      element.visitChildren(visit);
  }
  visit(this);
  return result;
}

1.3.4 Layers

Layers are the carriers of drawing commands and can cache the results. The base Layer class provides flags that indicate whether the layer or its subtree needs to be added to the scene.

abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
  bool get alwaysNeedsAddToScene => false;
  bool _needsAddToScene = true;
  void markNeedsAddToScene() { _needsAddToScene = true; }
  bool _subtreeNeedsAddToScene;
  void updateSubtreeNeedsAddToScene() {
    _subtreeNeedsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
  }
}

1.4 Flutter Rendering Process Breakdown

The full pipeline consists of six stages: Build, Diff, Layout, Paint, Composite, and Render. The article focuses on the four stages after Diff.

Build

During build , Flutter distinguishes between StatelessWidget (configuration‑only) and StatefulWidget (state‑driven). The latter uses a State object whose build method creates the UI.

Layout

Each RenderObject performs two actions: it calls its own performLayout to compute size, then it calls layout on its children, passing down constraints.

void performLayout() {
  _size = configuration.size;
  if (child != null) {
    child.layout(BoxConstraints.tight(_size));
  }
}

void layout(Constraints constraints, {bool parentUsesSize = false}) {
  // ... omitted logic
  performLayout();
  markNeedsSemanticsUpdate();
  _needsLayout = false;
  markNeedsPaint();
}

Paint

The paint phase traverses the RenderObject tree, invoking paint on each node. If a node implements CustomPainter , its paint method is called first.

void paint(PaintingContext context, Offset offset) {
  if (_painter != null) {
    _paintWithPainter(context.canvas, offset, _painter);
    _setRasterCacheHints(context);
  }
  super.paint(context, offset);
  if (_foregroundPainter != null) {
    _paintWithPainter(context.canvas, offset, _foregroundPainter);
    _setRasterCacheHints(context);
  }
}

Composite

All layers are combined into a Scene , which is submitted to the engine via ui.window.render . The engine finally draws the scene on the screen.

final ui.Window _window;
void compositeFrame() {
  final ui.SceneBuilder builder = ui.SceneBuilder();
  final ui.Scene scene = layer.buildScene(builder);
  _window.render(scene);
  scene.dispose();
}

2. Cross‑Platform Solution Comparison

Three mainstream approaches are discussed:

Web‑container solutions (WebView, H5, Cordova, WeChat Mini‑Program). Simple and support hot‑update, but suffer from performance and interaction gaps compared with native.

Generic Web‑container solutions (React Native, Weex, Hippy). Use native controls for better interaction, support offline bundles, but still rely on a JavaScript bridge that can cause latency and platform‑specific UI differences.

Self‑draw engine solutions (Flutter). Render directly with Skia/OpenGL, offering high performance and consistent UI across platforms. Communication uses MethodChannel , BasicMessageChannel , and EventChannel , which are faster than typical JS bridges.

3. Flutter Hybrid Development Modes

Two structural patterns are presented:

Unified management mode : A standard Flutter application where the ios/ and android/ directories are mixed with native code. Suitable when Flutter drives most business logic.

Three‑side separation mode : Flutter is built as a module (AAR for Android, Framework/Pod for iOS) and integrated into native projects like any third‑party SDK.

Navigation between native and Flutter pages involves four scenarios: Native→Flutter, Flutter→Flutter, Flutter→Native, Native→Native. The article details the implementation of each, including sample Objective‑C code for launching a FlutterViewController :

- (void)showFlutter {
  FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
  [self presentViewController:flutterViewController animated:YES completion:nil];
}

3.3 Hybrid Frameworks

Four representative solutions are compared:

Flutter Boost : Uses a WebView‑like navigation stack; each Flutter→Flutter transition creates a new FlutterViewController , which incurs extra memory.

Flutter Thrio : Combines native navigation with Flutter’s own Navigator , reducing memory overhead but increasing implementation complexity.

Multi‑Engine mode : Multiple Flutter engines run in the same app, each isolated. Provides strong module isolation but leads to heavy resource consumption and complex communication.

View‑level hybrid mode : Introduces a windowId so that several windows can share a single root isolate, achieving memory sharing while keeping multiple FlutterViewController instances.

4. Engineering Exploration

4.1 Compilation Modes

Dart supports JIT (Just‑In‑Time) for Debug builds and AOT (Ahead‑Of‑Time) for Release builds. Flutter apps can run in three modes:

Debug – JIT, hot‑reload, rich debugging information.

Release – AOT, optimized binary, best performance.

Profile – AOT with profiling hooks, used for performance analysis.

4.2 Integration and Build

Two integration approaches are described:

Source integration : Directly include the Flutter module source in the native project (used during development on the dev branch).

Artifact integration : Publish compiled AAR (Android) or Framework/Pod (iOS) and consume them via Maven or CocoaPods in other branches. Only Release artifacts are published because Debug/Profile artifacts cannot be debugged when integrated as binaries.

4.3 Workflow

The article concludes with a visual workflow of the "Fan Live" Flutter hybrid project and points to further reading on Flutter hybrid development and Flutter Boost source analysis.

Extended reading: Flutter Hybrid Development Mode Exploration Flutter Boost Hybrid Development Practice and Source Analysis
DartFluttermobile developmentcross‑platformRenderinghybrid-developmentengine
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

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.