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

相关推荐
IntMainJhy20 小时前
【flutter for open harmony】第三方库Flutter 国际化多语言的鸿蒙化适配与实战指南
数据库·flutter·华为·sqlite·harmonyos
南村群童欺我老无力.20 小时前
鸿蒙动画系统的常见陷阱与性能优化
华为·性能优化·harmonyos
liulian091620 小时前
【Flutter for OpenHarmony 】地图功能适配与位置显示实现指南
flutter·华为·学习方法·harmonyos
IntMainJhy20 小时前
【flutter for open harmony】Flutter SQLite 本地数据库的鸿蒙化适配与实战指南
数据库·flutter·sqlite
IntMainJhy20 小时前
【flutter for open harmony】第三方库「Flutter 聊天组件鸿蒙化适配与实战:从零搭建鸿蒙跨平台聊天页面」
flutter·华为·harmonyos
key_3_feng21 小时前
HarmonyOS NEXT开发环境搭建深度方案
华为·harmonyos
苗俊祥21 小时前
沐界浏览器-轻量 · 多标签 · 为鸿蒙设备打造的网页浏览体验*
华为·harmonyos·鸿蒙
jiejiejiejie_21 小时前
Flutter for OpenHarmony 地图功能萌系实战指南:给 App 加上超萌 “小地图”✨
flutter·华为·harmonyos
南村群童欺我老无力.21 小时前
鸿蒙网络请求的错误处理与超时管理
网络·华为·harmonyos
jiejiejiejie_21 小时前
Flutter for OpenHarmony 页面导航与动效库适配小记复盘:让 App 又丝滑又灵动✨
flutter·华为·harmonyos