鸿蒙 Flutter 多引擎场景开发指导

鸿蒙 Flutter 多引擎场景开发指导

欢迎大家加入开源鸿蒙跨平台开发者社区:https://openharmonycrossplatform.csdn.net/

在鸿蒙 Flutter 应用中,多引擎指同时存在多个 Flutter 引擎实例,每个引擎可绑定不同的 Dart 入口(entrypoint),实现多页面/多业务隔离、独立生命周期与路由。适用于多 Tab、多业务模块、或需与原生页面混合导航的复杂场景。

本文介绍从创建工程、Flutter 端路由配置到 ArkTS 端多引擎创建与集成的完整流程。


一、前置条件


二、创建 Flutter 应用

在目标目录执行:

bash 复制代码
flutter create demo
cd demo

确保工程已包含 ohos 平台(若无则执行 flutter create --platforms ohos .)。


三、Flutter 端:多页面与路由配置

1. 创建多个 Dart 页面与入口

为每个「引擎」准备独立页面及可选独立入口(entrypoint)。例如:

  • lib/main.dart:主入口,定义路由表
  • lib/page1/main.dart:页面 1 的入口(可选独立 entrypoint)
  • lib/page2/main.dart:页面 2 的入口(可选独立 entrypoint)

每个子页面可为独立 runApp(...) 的 mini-app,或在主入口中通过路由统一管理。

2. 在 lib/main.dart 中定义路由

主入口中注册多页面路由,供 ArkTS 通过 pushRoute 跳转:

dart 复制代码
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '多引擎集成',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      routes: <String, WidgetBuilder>{
        '/': (context) => HomePage(),
        '/page1': (context) => const page1.MyApp(),
        '/page2': (context) => const page2.MyApp(),
      },
    );
  }
}

路由名(如 /page1/page2)需与 ArkTS 端 FlutterEngineCache 的 key 及 pushRoute 参数一致。


四、ArkTS 端:多引擎创建与缓存

1. 使用 FlutterEngineGroup + FlutterEngineCache

在 ArkTS 中创建 FlutterEngineGroup ,为每个业务入口创建独立 FlutterEngine ,并用 FlutterEngineCache 按 key 缓存,便于后续页面按 key 取用。

示例:entry/src/main/ets/pages/Index.ets

typescript 复制代码
import { router } from '@kit.ArkUI';
import common from '@ohos.app.ability.common';
import { FlutterEngineCache, FlutterEngineGroup } from '@ohos/flutter_ohos';

let storage = LocalStorage.getShared();
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS';

@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext;
  private flutterEngineGroup?: FlutterEngineGroup;
  @LocalStorageLink('viewId') viewId: string = "";

  async aboutToAppear(): Promise<void> {
    await this.createAndCacheEngine();
  }

  private async createAndCacheEngine(): Promise<void> {
    this.flutterEngineGroup = new FlutterEngineGroup();

    interface EngineConfig {
      key: string;
      entrypoint: string;
    }

    const engineConfigs: EngineConfig[] = [
      { key: "/page1", entrypoint: "lib/page1/main.dart" },
      { key: "/page2", entrypoint: "lib/page2/main.dart" },
    ];

    for (const config of engineConfigs) {
      if (!FlutterEngineCache.getInstance().contains(config.key)) {
        try {
          let flutterEngine = await this.flutterEngineGroup!.createEngine(
            getContext(this) as common.UIAbilityContext,
            { entrypoint: config.entrypoint }
          );
          await flutterEngine.init(getContext(this), null, false);
          FlutterEngineCache.getInstance().put(config.key, flutterEngine);
        } catch (error) {
          console.error(`create engine failed: ${config.key}`, error);
        }
      }
    }
  }

  build() {
    Stack() {
      Column() {
        Button('跳转Page1')
          .onClick(() => {
            try {
              FlutterEngineCache.getInstance().get('/page1')?.getNavigationChannel()?.pushRoute('/page1');
              router.pushUrl({ url: 'pages/Flutter', params: { route: '/page1' } });
            } catch (err) {
              console.error('push page1 failed', err);
            }
          });

        Button('跳转Page2')
          .onClick(() => {
            try {
              FlutterEngineCache.getInstance().get('/page2')?.getNavigationChannel()?.pushRoute('/page2');
              router.pushUrl({ url: 'pages/Flutter', params: { route: '/page2' } });
            } catch (err) {
              console.error('push page2 failed', err);
            }
          });
      }
    }
    .width('100%')
    .height('100%')
    .align(Alignment.Center);
  }

  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS);
    return true;
  }
}

要点:

  • key :与 Flutter 路由名一致(如 /page1),用于缓存与取回引擎。
  • entrypoint :对应 Dart 入口(如 lib/page1/main.dart),若使用单入口多路由则可与主入口一致,仅 key 区分。
  • createEngine :若当前 SDK 支持传入 entrypoint,可传 { entrypoint: config.entrypoint };若不支持则传 null,引擎使用默认入口,仅通过后续 pushRoute 区分页面。

2. 新建 Flutter 容器页并挂载缓存引擎

entry/src/main/ets/pages 下新建 Flutter.ets ,作为承载 Flutter 页面的容器:从路由参数中读取 route,用其作为 cached_engine_id 从缓存中取引擎并挂载到 FlutterView。

entry/src/main/ets/pages/Flutter.ets 示例:

typescript 复制代码
import router from '@ohos.router';
import { FlutterPage } from '@ohos/flutter_ohos';
import { FlutterView } from '@ohos/flutter_ohos/src/main/ets/view/FlutterView';
import FlutterEntry from '@ohos/flutter_ohos/src/main/ets/embedding/ohos/FlutterEntry';
import MyFlutterEntry from '../entryability/MyFlutterEntry';

@Entry
@Component
struct FlutterPageContainer {
  private flutterEntry: FlutterEntry | null = null;
  private flutterView: FlutterView | undefined = undefined;

  aboutToAppear(): void {
    const context = getContext(this) as common.UIAbilityContext;
    const params = router.getParams() as Record<string, Object>;
    params["should_attach_engine_to_ability"] = true;
    const routeName = params["route"] as string;
    params["cached_engine_id"] = routeName;
    this.flutterEntry = new MyFlutterEntry(context, params);
    this.flutterEntry!.aboutToAppear();
    this.flutterView = this.flutterEntry!.getFlutterView();
  }

  aboutToDisappear(): void {
    this.flutterEntry?.aboutToDisappear();
  }

  onPageShow(): void {
    this.flutterEntry?.onPageShow();
  }

  onPageHide(): void {
    this.flutterEntry?.onPageHide();
  }

  build() {
    Stack() {
      if (this.flutterView) {
        FlutterPage({ viewId: this.flutterView.getId() });
      }
    }
  }

  onBackPress(): boolean {
    return this.flutterEntry?.onBackPress() ?? false;
  }
}

说明:

  • route :由 Index 页 router.pushUrl({ url: 'pages/Flutter', params: { route: '/page1' } }) 传入,与引擎 key 一致。
  • cached_engine_id :设为同一 route,使 FlutterEntry 从 FlutterEngineCache 中取出对应引擎并挂载。
  • MyFlutterEntry:需在工程中实现,用于根据 params 创建/获取 Flutter 容器并绑定缓存引擎;若无现成实现,可参考官方示例或 SDK 中的 FlutterEntry 用法。

五、流程小结与注意点

步骤 位置 要点
1 Flutter 多页面 + 路由名(如 /page1/page2)与 ArkTS 端 key 一致
2 ArkTS Index FlutterEngineGroup 创建多引擎,按 key + entrypoint 配置并放入 FlutterEngineCache
3 ArkTS Index 跳转前对对应 key 的引擎 getNavigationChannel()?.pushRoute(route),再 router.pushUrl 到 Flutter.ets 并传 params: { route }
4 ArkTS Flutter.ets params.route 作为 cached_engine_id,从缓存挂载引擎并展示 FlutterPage

注意:

  • 引擎数量与内存占用正相关,按业务需要创建,避免过多引擎常驻。
  • 若 SDK 的 createEngine 支持传入 entrypoint,建议显式传入,与 Flutter 侧入口一致。
  • 路由名、Cache key、cached_engine_idpushRoute 参数需保持一致,否则会取错引擎或无法显示正确页面。
  • 官方示例或 SDK 升级可能导致 API 略有差异(如 createEngine 参数、FlutterEntry 构造),请以当前使用的 Flutter OH 版本文档为准。

六、参考

以上示例展示了多引擎从 Flutter 路由到 ArkTS 创建、缓存与页面挂载的完整实现方式,可按项目需要增删引擎与路由。

相关推荐
ITKEY_2 小时前
flutter 在iPad mini7上真机运行实战(踩坑)
flutter·ios·ipad
恋猫de小郭2 小时前
Android 性能迎来提升:内核引入 AutoFDO 普惠所有 15-16 设备
android·前端·flutter
Justin在掘金2 小时前
Flutter Engine、Dart VM、Runner、鸿蒙端进程与线程 —— 深度解析
flutter
小雨青年3 小时前
鸿蒙 HarmonyOS 6 | 多媒体(05)全局播控 AVSession 接入与后台控制
华为·harmonyos
Keya3 小时前
鸿蒙平台实现高斯模糊的渐变色
harmonyos
大雷神4 小时前
HarmonyOS APP<玩转React>开源教程四:状态管理基础
react.js·开源·harmonyos
前端不太难5 小时前
90% 的鸿蒙 App,没有真正的依赖管理
华为·状态模式·harmonyos
江湖有缘6 小时前
基于华为openEuler系统部署MicroBin粘贴板工具
华为·docker·华为云·openeuler
程序员Ctrl喵6 小时前
渲染流水线:从代码到像素的“非凡旅程”
flutter