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

简介: 本文介绍了在Flutter三端分离模式下,将纯血鸿蒙混入Flutter项目的实践经验。基于咸鱼团队的flutter_boost和自定义FlutterPlugin实现,涵盖环境搭建、Flutter模块创建、flutter_boost集成、鸿蒙侧适配、双端通信及原生调用等内容。详细说明了Flutter与鸿蒙间的页面跳转、数据传递及方法调用的实现方式,为开发者提供参考。总结指出,通过管理页面栈和实现双端交互,可满足常规开发需求。

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

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

主要涉及内容:

环境搭建
Flutter module 创建
Futter 引入 flutter_boost
Harmony 引入 flutter_boost
Flutter 与鸿蒙侧通信
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
1
打包 Fluter 项目

flutter build har --debug
1
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});

@override
State createState() => _MyAppState();
}

class _MyAppState extends State {
// 3. 路由表
Map 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 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)!;
}

@override
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
@Component
struct 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 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');
1
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 FlutterBoost
const 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页面栈+鸿蒙页面栈跳转,搞定数据通信和原生调用,基本可以满足一般的开发,以上这里仅供参考哈~
————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/zsgzsgzsgzsgzsg/article/details/146200522

相关文章
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
3月前
|
存储 IDE 定位技术
【HarmonyOS 5】鸿蒙组件&模板服务详解 - 助力高效开发的利器
在移动应用开发领域,效率与质量始终是开发者追求的核心目标。鸿蒙系统作为新兴的操作系统,为开发者提供了丰富且强大的开发资源,其中鸿蒙组件&模板服务更是成为开发者快速构建高质量应用的得力助手。
126 0
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
|
2月前
|
存储 SQL 前端开发
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
本节以“账本”为例,使用关系型数据库接口实现账单的增、删、改、查操作。通过创建ArkTSRdb应用,演示如何操作RdbStore进行数据管理,并结合界面按钮实现交互功能。
111 0
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
HarmonyOS NEXT仓颉开发语言实战案例:图片预览器
本文介绍了如何使用仓颉语言实现图片放大预览器。通过弹窗组件`CustomDialogController`与`Swiper`容器结合,实现全屏图片浏览效果,支持多图切换与点击关闭功能,适配动态广场场景下的图片预览需求。
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:动态广场
本文介绍了使用仓颉语言开发类似朋友圈的动态广场页面,包含导航栏和状态列表。通过Column、Row、List等组件实现页面结构,并使用Grid适配图片展示。结合数据绑定与组件化思想,完成动态内容展示。
|
3月前
|
前端开发
HarmonyOS NEXT仓颉开发语言实现画板案例
本文介绍了使用仓颉开发语言实现画板功能的案例,通过Canvas组件实现画布绘制,并利用贝塞尔曲线使画笔跟随触摸轨迹生成平滑曲线。内容包含画布初始化、触控事件处理及清空功能,展示了仓颉语言在图形绘制中的应用技巧。
|
Android开发
HarmonyOS(鸿蒙)——长按事件
HarmonyOS(鸿蒙)——长按事件
505 0
HarmonyOS(鸿蒙)——长按事件
|
3月前
|
开发者
鸿蒙开发:资讯项目实战之项目初始化搭建
目前来说,我们的资讯项目只是往前迈了很小的一步,仅仅实现了项目创建,步虽小,但概念性的知识很多,这也是这个项目的初衷,让大家不仅仅可以掌握日常的技术开发,也能让大家理解实际的项目开发知识。
鸿蒙开发:资讯项目实战之项目初始化搭建
|
3月前
|
缓存 JavaScript IDE
鸿蒙开发:基于最新API,如何实现组件化运行
手动只是让大家了解切换的原理,在实际开发中,可不推荐手动,下篇文章,我们将通过脚本或者插件,快速实现组件化模块之间的切换,实现独立运行,敬请期待!
117 0
鸿蒙开发:基于最新API,如何实现组件化运行

热门文章

最新文章