> 技术文档 > HarmonyOS NEXT-Flutter混合开发之鸿蒙-代码实践_flutter 鸿蒙

HarmonyOS NEXT-Flutter混合开发之鸿蒙-代码实践_flutter 鸿蒙

在 Flutter 三端分离模式下完成纯血鸿蒙混入的过程中,虽然官方文档提供了一定的指导,但实际操作中可能会遇到一些坑。以下是我在适配过程中的一些经验总结,供各位开发者参考 😄 如果有帮助点个赞。

在混入过程中是基于咸鱼团队 flutter_boost(这里不讨论和其他方案的差别) 和自定义 FlutterPlugin 实现的。

主要涉及内容:

  1. 环境搭建
  2. Flutter module 创建
  3. Futter 引入 flutter_boost
  4. Harmony 引入 flutter_boost
  5. Flutter 与鸿蒙侧通信
  6. Flutter 调用鸿蒙原生

环境搭建

  • Fluter 环境

准备支持鸿蒙的 Flutter 开发环境,flutter_fluter 仓库基于 Flutter SDK 对于OpenHarmony平台的兼容拓展,可支持 IDE 或者终端使用 Flutter Tools 指令编译和构建 OpenHarmony 应用程序。

  • Harmonyos NEXT 环境

不再赘述,上链接。

Flutter module 创建

创建 Fluter 项目

flutter create -t module --org xyz.zhousg demo_fluter 

打包 Fluter 项目

flutter build har --debug

Fluter 引入 flutter_boost

  • 安装依赖
dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 fl_chart: ^0.62.0 flutter_boost:+ git:+ url: \'https://github.com/alibaba/flutter_boost.git\'+ ref: \'4.6.5\'
  • 配置路由表
import \'package:flutter/cupertino.dart\';import \'package:flutter/material.dart\';import \'package:flutter_boost/flutter_boost.dart\';// 1. 创建一个自定义的Binding,继承和with的关系如下,里面什么都不用写class CustomFlutterBinding extends WidgetsFlutterBinding with BoostFlutterBinding {}void main() { // 2. 这里的CustomFlutterBinding调用务必不可缺少,用于控制Boost状态的resume和pause CustomFlutterBinding(); runApp(const MyApp());}class MyApp extends StatefulWidget { const MyApp({super.key});  State<MyApp> createState() => _MyAppState();}class _MyAppState extends State<MyApp> { // 3. 路由表 Map<String, FlutterBoostRouteFactory> routerMap = { \'SettingsPage\': (settings, isContainerPage, uniqueId) { return CupertinoPageRoute( settings: settings, builder: (BuildContext ctx) { return const Placeholder(); }, ); }, \'DeviceStoragePage\': (settings, isContainerPage, uniqueId) { return CupertinoPageRoute( settings: settings, builder: (BuildContext ctx) { return const Placeholder(); }, ); }, \'AboutPage\': (settings, isContainerPage, uniqueId) { return CupertinoPageRoute( settings: settings, builder: (BuildContext ctx) { return const Placeholder(); }, ); }, \'/\': (settings, isContainerPage, uniqueId) { return CupertinoPageRoute( settings: settings, builder: (BuildContext ctx) { return const Placeholder(); }, ); }, }; // 路由工厂函数 Route<dynamic> routeFactory( RouteSettings settings, bool isContainerPage, String? uniqueId) { FlutterBoostRouteFactory? fn = routerMap[settings.name]; if (fn == null) { throw FlutterError( \'Route \"${settings.toString()}\" is not defined in routerMap.\'); } return fn(settings, isContainerPage, uniqueId)!; }  Widget build(BuildContext context) { // flutter_boost 接管 return FlutterBoostApp( routeFactory, // Flutter 侧直接预览需要,需要使用 Deveco Studio 导入 .ohos 项目进行自动签名预览至鸿蒙设备 initialRoute: \'SettingsPage\', appBuilder: (home) { return MaterialApp( builder: (context, child) => home, ); }, ); }}

Harmony 引入 flutter_boost

这里使用的是 router + FlutterPage 方式展示 Flutter 界面, Navigation 后续再说吧~

a. 先打包 Fluter 项目,会生成三个产物

.ohos |--har |-- fluter_boost.har |-- fluter_module.har |-- fluter.har

b. 在鸿蒙项目中,引入依赖

oh-package.json5

\"dependencies\": { \"@ohos/flutter_module\": \"file:../demo_flutter/.ohos/har/flutter_module.har\", // 下面两个依赖我直接拷贝到了 libs 下 \"@ohos/flutter_ohos\": \"file:./libs/flutter.har\", \"flutter_boost\": \"file:./libs/flutter_boost.har\"},\"overrides\": { \"@ohos/flutter_ohos\": \"file:./libs/flutter.har\", \"flutter_boost\": \"file:./libs/flutter_boost.har\"},

c. 初始化 flutter_boost

entryAbility.ets

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from \'@kit.AbilityKit\';import { router } from \'@kit.ArkUI\';import { FlutterManager } from \'@ohos/flutter_ohos\';import { FlutterBoostDelegate, FlutterBoostRouteOptions, FlutterBoost, FlutterBoostSetupOptionsBuilder } from \'flutter_boost\';import { GeneratedPluginRegistrant } from \'@ohos/flutter_module\';export default class EntryAbility extends UIAbility implements FlutterBoostDelegate{ pushNativeRoute(options: FlutterBoostRouteOptions): void { // throw new Error(\'Method not implemented.\'); } pushFlutterRoute(options: FlutterBoostRouteOptions,): void { // throw new Error(\'Method not implemented.\'); } popRoute(options: FlutterBoostRouteOptions): boolean { // throw new Error(\'Method not implemented.\'); router.back() return true } async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { FlutterManager.getInstance().pushUIAbility(this); } onDestroy(): void { FlutterManager.getInstance().popUIAbility(this); } onWindowStageCreate(windowStage: window.WindowStage): void { // Flutter bind in UIAbility FlutterManager.getInstance().pushWindowStage(this, windowStage); // Initial FlutterBoost const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder() FlutterBoost.getInstance().setup(this, this.context, (engine) => { GeneratedPluginRegistrant.registerWith(engine) }, optionsBuilder.build()) windowStage.loadContent(\'pages/Index\'); } onWindowStageDestroy(): void { FlutterManager.getInstance().popWindowStage(this); } onForeground(): void { logger.info(\'Ability onForeground\'); } onBackground(): void { logger.info(\'Ability onBackground\'); }}

这里部分代码省略了 ~ pushNativeRoute pushFlutterRoute popRoute 具体实现还是参考官方仓库

d. Flutter容器与跳转

  • FlutterPage 承载Flutter的页面

pages/FluterPage.ets

import { FlutterEntry, FlutterPage, FlutterView} from \'@ohos/flutter_ohos\';import { FlutterBoost, FlutterBoostEntry } from \'flutter_boost\';import { router } from \'@kit.ArkUI\';@Entry@Componentstruct SettingsPage { private flutterEntry?: FlutterEntry; private flutterView?: FlutterView aboutToAppear() { this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams()) this.flutterEntry?.aboutToAppear() this.flutterView = this.flutterEntry?.getFlutterView() } aboutToDisappear() { this.flutterEntry?.aboutToDisappear() } onPageShow() { this.flutterEntry?.onPageShow() } onPageHide() { this.flutterEntry?.onPageHide() } onBackPress(): boolean | void { FlutterBoost.getInstance() .getPlugin()?.onBackPressed(); return true; } build() { Column() { FlutterPage({ viewId: this.flutterView?.getId() }) .width(\'100%\') .height(\'100%\') } .width(\'100%\') .height(\'100%\') }}
  • 在 Index 中的跳转

pages/Index.ets

// uri Flutter Module 中的路由表 KEY params 是传参router.pushUrl({ url: \'pages/FlutterPage\', params: { uri: \'DeviceStoragePage\', params: {} } })router.pushUrl({ url: \'pages/FlutterPage\', params: { uri: \'SettingPage\', params: {} } })router.pushUrl({ url: \'pages/FlutterPage\', params: { uri: \'AboutPage\', params: {} } })

Flutter 与鸿蒙侧通信

  • Flutter侧
 TextButton( onPressed: () { Map<String, String> data = {\'name\': \'jack\'}; BoostChannel.instance.sendEventToNative(\'updateUser\', data); }, child: const Text(\'发送消息\'), ),
  • 鸿蒙侧

pages/FlutterPage.ets

aboutToAppear() { this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams()) this.flutterEntry?.aboutToAppear() this.flutterView = this.flutterEntry?.getFlutterView() const plugin = FlutterBoost.getInstance() .getPlugin() if (plugin) { // 通信 plugin.addEventListener(\'updateUser\', { onEvent: (key, args) => { // logger.debug(`事件名称 ${key}`, JSON.stringify(args)) promptAction.showToast({ message: \'Flutter Data: \' + JSON.stringify(args) }) } }) }

Flutter 调用鸿蒙原生

  • Flutter侧
final _platform = const MethodChannel(\'xyz.zhousg.interview_success_project\');
TextButton( onPressed: () { _platform.invokeMethod(\'openCamera\').then( (value) => setState(() {  // value 鸿蒙侧回传数据 }), ); }, child: Text(\'打开相机),),
  • 鸿蒙侧

定义 Flutter 插件

NativePlugin.ets

import { FlutterPlugin, FlutterPluginBinding, MethodChannel, MethodResult } from \'@ohos/flutter_ohos\';export class NativePlugin implements FlutterPlugin { private channel?: MethodChannel; getUniqueClassName(): string { return \'CameraPlugin\' } onAttachedToEngine(binding: FlutterPluginBinding): void { this.channel = new MethodChannel(binding.getBinaryMessenger(), \'xyz.zhousg.interview_success_project\') this.channel.setMethodCallHandler({ onMethodCall: (call, result) => { switch (call.method) { case \"openCamera\": this.openCamera(result) break; default: result.notImplemented() break; } } }) } onDetachedFromEngine(binding: FlutterPluginBinding): void { this.channel?.setMethodCallHandler(null); } // native api openCamera (result: MethodResult) { // 回传数据给 Flutter result.success(\'http://test.png\') }}

注册插件

entryAbility.ets

// Initial FlutterBoostconst optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()FlutterBoost.getInstance().setup(this, this.context, (engine) => { GeneratedPluginRegistrant.registerWith(engine) // 打开相机 engine.getPlugins()?.add(new NativePlugin())}, optionsBuilder.build())

总结

使用 flutter_boost 开发 Flutter混合项目,在鸿蒙这边和混合 Web 组件进行混合开发很相似,搞定Flutter页面栈+鸿蒙页面栈跳转,搞定数据通信和原生调用,基本可以满足一般的开发,以上这里仅供参考哈~