鸿蒙 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 创建、缓存与页面挂载的完整实现方式,可按项目需要增删引擎与路由。

相关推荐
烛衔溟3 分钟前
HarmonyOS 基础 UI 构建 —— 组件、布局与沉浸式效果
ui·华为·harmonyos
不爱吃糖的程序媛26 分钟前
React Native 三方库 react-native-share 的 HarmonyOS 适配实战
react native·react.js·harmonyos
TrisighT26 分钟前
Electron 的 printToPDF 在鸿蒙 PC 上翻车了,我换了个纯前端方案绕过去
electron·harmonyos
高心星27 分钟前
鸿蒙6.0应用开发——实况窗开发
华为·通知·鸿蒙6.0·harmonyos6.0·实况窗
李二。29 分钟前
ArkTS 系统监控面板:从零构建 HarmonyOS PC 端实时监控工具
华为·harmonyos
nashane30 分钟前
HarmonyOS 6学习:指南针“文图反向”Bug修复——从“北偏东”变“北偏西”的坐标系纠错
学习·华为·bug·harmonyos
慧海灵舟32 分钟前
鸿蒙南向开发教程Day1:Hi3861 开发环境配置完全指南
华为·harmonyos·写文章,赢小鸿ai
禁默33 分钟前
[鸿蒙PC命令行移植适配]移植rust三方库eza到鸿蒙PC的完整实践
华为·rust·harmonyos
喵了几个咪37 分钟前
基于 Flutter 的 Headless CMS 全平台前端架构:技术解析与二次开发导引
前端·flutter·架构
不爱吃糖的程序媛1 小时前
React Native 应用适配鸿蒙PC 实战:从白屏到成功运行
react native·react.js·harmonyos