Mobile Development 13 min read

Porting Flutter to HarmonyOS: Technical Exploration and Implementation

Meituan’s MTFlutter team rebuilt Flutter’s embedder layer for HarmonyOS by simulating VSync, creating a SurfaceProvider‑based rendering surface, forwarding touch, key and speech events, and re‑implementing asset loading, message loops and lifecycle callbacks, allowing Flutter apps to run on phones, tablets, TVs and wearables.

Meituan Technology Team
Meituan Technology Team
Meituan Technology Team
Porting Flutter to HarmonyOS: Technical Exploration and Implementation

HarmonyOS is Huawei's distributed operating system. To port Flutter applications with low cost, Meituan's MTFlutter team explored native support for HarmonyOS.

Flutter consists of three layers: the framework layer (written in Dart) for business logic, the engine layer (C/C++) that provides rendering and the Dart runtime, and the embedder layer that interacts with the underlying platform. The adaptation focuses on re‑implementing the embedder layer for HarmonyOS.

Key tasks include implementing VSync handling, constructing a rendering surface, forwarding input events, and providing other essential platform services.

VSync handling : In Android, VSync is obtained via Choreographer. The original Java implementation is reproduced below.

@Override
public void asyncWaitForVsync(long cookie) {
  Choreographer.getInstance()
      .postFrameCallback(
          new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
              float fps = windowManager.getDefaultDisplay().getRefreshRate();
              long refreshPeriodNanos = (long) (1000000000.0 / fps);
              FlutterJNI.nativeOnVsync(
                  frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
            }
          });
}

Since HarmonyOS does not provide a Choreographer‑like API, a simulated VSync using the UI task dispatcher is employed:

@Override
public void asyncWaitForVsync(long cookie) {
  // Simulate 60 fps screen refresh interval: dispatch an async task to the UI thread after 16 ms
  applicationContext.getUITaskDispatcher().delayDispatch(() -> {
    float fps = 60; // HarmonyOS does not expose frame‑rate API, assume 60 fps
    long refreshPeriodNanos = (long) (1000000000.0 / fps);
    long frameTimeNanos = System.nanoTime();
    FlutterJNI.nativeOnVsync(frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
  }, 16);
}

Rendering surface : A view component that provides a raw Surface is required. HarmonyOS offers SurfaceProvider to manage surfaces.

// Create a container component that manages a Surface
SurfaceProvider surfaceProvider = new SurfaceProvider(context);
// Register view‑creation callback
surfaceProvider.getSurfaceOps().get().addCallback(surfaceCallback);

@Override
public void surfaceCreated(SurfaceOps surfaceOps) {
  Surface surface = surfaceOps.getSurface();
  // Pass the surface to the native side via JNI
  FlutterJNI.onSurfaceCreated(surface);
}

The native side converts the surface to an EGL window surface:

// window->handle() is the previously obtained NativeLayer
EGLSurface surface = eglCreateWindowSurface(
    display, config_, reinterpret_cast<EGLNativeWindowType>(window->handle()), attribs);
// ... hand over to Flutter's rendering pipeline

Input interaction : Touch, key, and speech events are captured via HarmonyOS multi‑modal APIs and forwarded to the Flutter engine.

flutterComponent.setTouchEventListener(touchEventListener); // touch & mouse events
flutterComponent.setKeyEventListener(keyEventListener); // keyboard events
flutterComponent.setSpeechEventListener(speechEventListener); // speech input events

Other platform capabilities such as asset loading, message loop, and lifecycle callbacks are re‑implemented using HarmonyOS APIs. For example, asset mapping using RawFile:

class HAPAssetMapping : public fml::Mapping {
 public:
  HAPAssetMapping(RawFile* asset) : asset_(asset) {}
  ~HAPAssetMapping() override { CloseRawFile(asset_); }

  size_t GetSize() const override { return GetRawFileSize(asset_); }

  const uint8_t* GetMapping() const override {
    return reinterpret_cast<const uint8_t*>(GetRawFileBuffer(asset_));
  }

 private:
  RawFile* const asset_;
  FML_DISALLOW_COPY_AND_ASSIGN(HAPAssetMapping);
};

Message loop integration using HarmonyOS event runner:

void MessageLoopHarmony::Run() {
  FML_DCHECK(runner_ == GetEventRunnerNativeObjForThread());
  int result = ::EventRunnerRun(runner_);
  FML_DCHECK(result == 0);
}

void MessageLoopHarmony::Terminate() {
  int result = ::EventRunnerStop(runner_);
  FML_DCHECK(result == 0);
}

Lifecycle synchronization leverages HarmonyOS Page Ability callbacks to notify the Flutter engine of state changes.

After integrating these components, Flutter applications run on HarmonyOS devices—including phones, tablets, TVs, and wearables—as shown by the screenshots. The work demonstrates that with modest effort, Flutter’s embedder can be ported to a new OS, enabling cross‑device coverage and preparing for future distributed scenarios.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

FlutterMobile Developmentcross-platformHarmonyOSEmbedding
Meituan Technology Team
Written by

Meituan Technology Team

Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.

0 followers
Reader feedback

How this landed with the community

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.