查看: 113|回复: 0

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

[复制链接]

3

主题

2

回帖

20

积分

新手上路

积分
20
发表于 2025-4-1 08:53:24 | 显示全部楼层 |阅读模式
在 Flutter 三端分离模式下完成纯血鸿蒙混入的过程中,虽然官方文档提供了一定的指导,但实际操作中可能会遇到一些坑。以下是我在适配过程中的一些经验总结,供各位开发者参考 😄 如果有帮助点个赞。
在混入过程中是基于咸鱼团队 flutter_boost(这里不讨论和其他方案的差别) 和自定义 FlutterPlugin 实现的。
主要涉及内容:
    环境搭建Flutter module 创建Futter 引入 flutter_boostHarmony 引入 flutter_boostFlutter 与鸿蒙侧通信Flutter 调用鸿蒙原生
环境搭建

    Fluter 环境
准备支持鸿蒙的 Flutter 开发环境,flutter_fluter 仓库基于 Flutter SDK 对于 OpenHarmony 平台的兼容拓展,可支持 IDE 或者终端使用 Flutter Tools 指令编译和构建 OpenHarmony 应用程序。
    Harmonyos NEXT 环境
不再赘述,上链接。
Flutter module 创建

创建 Fluter 项目
  1. flutter create -t module --org xyz.zhousg demo_fluter
复制代码
打包 Fluter 项目
  1. flutter build har --debug
复制代码
Fluter 引入 flutter_boost

    安装依赖
  1. dependencies:
  2.   flutter:
  3.     sdk: flutter
  4.   # The following adds the Cupertino Icons font to your application.
  5.   # Use with the CupertinoIcons class for iOS style icons.
  6.   cupertino_icons: ^1.0.2
  7.   fl_chart: ^0.62.0
  8.   flutter_boost:
  9. +    git:
  10. +        url: 'https://github.com/alibaba/flutter_boost.git'
  11. +        ref: '4.6.5'
复制代码
    配置路由表
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_boost/flutter_boost.dart';
  4. // 1. 创建一个自定义的Binding,继承和with的关系如下,里面什么都不用写
  5. class CustomFlutterBinding extends WidgetsFlutterBinding
  6.     with BoostFlutterBinding {
  7.    }
  8. void main() {
  9.   // 2. 这里的CustomFlutterBinding调用务必不可缺少,用于控制Boost状态的resume和pause
  10.   CustomFlutterBinding();
  11.   runApp(const MyApp());
  12. }
  13. class MyApp extends StatefulWidget {
  14.   const MyApp({
  15.    super.key});
  16.   @override
  17.   State createState() => _MyAppState();
  18. }
  19. class _MyAppState extends State {
  20.   // 3. 路由表
  21.   Map routerMap = {
  22.     'SettingsPage': (settings, isContainerPage, uniqueId) {
  23.       return CupertinoPageRoute(
  24.         settings: settings,
  25.         builder: (BuildContext ctx) {
  26.           return const Placeholder();
  27.         },
  28.       );
  29.     },
  30.     'DeviceStoragePage': (settings, isContainerPage, uniqueId) {
  31.       return CupertinoPageRoute(
  32.         settings: settings,
  33.         builder: (BuildContext ctx) {
  34.           return const Placeholder();
  35.         },
  36.       );
  37.     },
  38.     'AboutPage': (settings, isContainerPage, uniqueId) {
  39.       return CupertinoPageRoute(
  40.         settings: settings,
  41.         builder: (BuildContext ctx) {
  42.           return const Placeholder();
  43.         },
  44.       );
  45.     },
  46.     '/': (settings, isContainerPage, uniqueId) {
  47.       return CupertinoPageRoute(
  48.         settings: settings,
  49.         builder: (BuildContext ctx) {
  50.           return const Placeholder();
  51.         },
  52.       );
  53.     },
  54.   };
  55.   // 路由工厂函数
  56.   Route routeFactory(
  57.       RouteSettings settings, bool isContainerPage, String? uniqueId) {
  58.     FlutterBoostRouteFactory? fn = routerMap[settings.name];
  59.     if (fn == null) {
  60.       throw FlutterError(
  61.           'Route "${ settings.toString()}" is not defined in routerMap.');
  62.     }
  63.     return fn(settings, isContainerPage, uniqueId)!;
  64.   }
  65.   @override
  66.   Widget build(BuildContext context) {
  67.     // flutter_boost 接管
  68.     return FlutterBoostApp(
  69.       routeFactory,
  70.       // Flutter 侧直接预览需要,需要使用 Deveco Studio 导入 .ohos 项目进行自动签名预览至鸿蒙设备
  71.       initialRoute: 'SettingsPage',
  72.       appBuilder: (home) {
  73.         return MaterialApp(
  74.           builder: (context, child) => home,
  75.         );
  76.       },
  77.     );
  78.   }
  79. }
复制代码
Harmony 引入 flutter_boost

这里使用的是 router + FlutterPage 方式展示 Flutter 界面,Navigation 后续再说吧~
a. 先打包 Fluter 项目,会生成三个产物
  1. .ohos
  2.    |--har
  3.        |-- fluter_boost.har
  4.        |-- fluter_module.har
  5.        |-- fluter.har
复制代码
b. 在鸿蒙项目中,引入依赖
oh-package.json5
  1. "dependencies": {
  2.   "@ohos/flutter_module": "file:../demo_flutter/.ohos/har/flutter_module.har",
  3.   // 下面两个依赖我直接拷贝到了 libs 下
  4.   "@ohos/flutter_ohos": "file:./libs/flutter.har",
  5.   "flutter_boost": "file:./libs/flutter_boost.har"
  6. },
  7. "overrides": {
  8.   "@ohos/flutter_ohos": "file:./libs/flutter.har",
  9.   "flutter_boost": "file:./libs/flutter_boost.har"
  10. },
复制代码
c. 初始化 flutter_boost
entryAbility.ets
  1. import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
  2. import { router } from '@kit.ArkUI';
  3. import { FlutterManager } from '@ohos/flutter_ohos';
  4. import { FlutterBoostDelegate, FlutterBoostRouteOptions, FlutterBoost, FlutterBoostSetupOptionsBuilder } from 'flutter_boost';
  5. import { GeneratedPluginRegistrant } from '@ohos/flutter_module';
  6. export default class EntryAbility extends UIAbility implements  FlutterBoostDelegate{
  7.   pushNativeRoute(options: FlutterBoostRouteOptions): void {
  8.     // throw new Error('Method not implemented.');
  9.   }
  10.   pushFlutterRoute(options: FlutterBoostRouteOptions,): void {
  11.     // throw new Error('Method not implemented.');
  12.   }
  13.   popRoute(options: FlutterBoostRouteOptions): boolean {
  14.     // throw new Error('Method not implemented.');
  15.     router.back()
  16.     return true
  17.   }
  18.   async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  19.     FlutterManager.getInstance().pushUIAbility(this);
  20.   }
  21.   onDestroy(): void {
  22.     FlutterManager.getInstance().popUIAbility(this);
  23.   }
  24.   onWindowStageCreate(windowStage: window.WindowStage): void {
  25.     // Flutter bind in UIAbility
  26.     FlutterManager.getInstance().pushWindowStage(this, windowStage);
  27.     // Initial FlutterBoost
  28.     const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()
  29.     FlutterBoost.getInstance().setup(this, this.context, (engine) => {
  30.       GeneratedPluginRegistrant.registerWith(engine)
  31.     }, optionsBuilder.build())
  32.     windowStage.loadContent('pages/Index');
  33.   }
  34.   onWindowStageDestroy(): void {
  35.     FlutterManager.getInstance().popWindowStage(this);
  36.   }
  37.   onForeground(): void {
  38.     logger.info('Ability onForeground');
  39.   }
  40.   onBackground(): void {
  41.     logger.info('Ability onBackground');
  42.   }
  43. }
复制代码
这里部分代码省略了~pushNativeRoute pushFlutterRoute popRoute 具体实现还是参考官方仓库
d. Flutter 容器与跳转
    FlutterPage 承载 Flutter 的页面
pages/FluterPage.ets
  1. import {
  2.     FlutterEntry, FlutterPage, FlutterView} from '@ohos/flutter_ohos';
  3. import {
  4.     FlutterBoost, FlutterBoostEntry } from 'flutter_boost';
  5. import {
  6.     router } from '@kit.ArkUI';
  7. @Entry
  8. @Component
  9. struct SettingsPage {
  10.   private flutterEntry?: FlutterEntry;
  11.   private flutterView?: FlutterView
  12.   aboutToAppear() {
  13.     this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams())
  14.     this.flutterEntry?.aboutToAppear()
  15.     this.flutterView = this.flutterEntry?.getFlutterView()
  16.   }
  17.   aboutToDisappear() {
  18.     this.flutterEntry?.aboutToDisappear()
  19.   }
  20.   onPageShow() {
  21.     this.flutterEntry?.onPageShow()
  22.   }
  23.   onPageHide() {
  24.     this.flutterEntry?.onPageHide()
  25.   }
  26.   onBackPress(): boolean | void {
  27.     FlutterBoost.getInstance()
  28.       .getPlugin()?.onBackPressed();
  29.     return true;
  30.   }
  31.   build() {
  32.     Column() {
  33.       FlutterPage({
  34.     viewId: this.flutterView?.getId() })
  35.         .width('100%')
  36.         .height('100%')
  37.     }
  38.     .width('100%')
  39.     .height('100%')
  40.   }
  41. }
复制代码
    在 Index 中的跳转
pages/Index.ets
  1. // uri Flutter Module 中的路由表 KEY params 是传参
  2. router.pushUrl({
  3.     url: 'pages/FlutterPage', params: {
  4.     uri: 'DeviceStoragePage', params: {
  5.    } } })
  6. router.pushUrl({
  7.     url: 'pages/FlutterPage', params: {
  8.     uri: 'SettingPage', params: {
  9.    } } })
  10. router.pushUrl({
  11.     url: 'pages/FlutterPage', params: {
  12.     uri: 'AboutPage', params: {
  13.    } } })
复制代码
Flutter 与鸿蒙侧通信

    Flutter 侧
  1. TextButton(
  2.     onPressed: () {
  3.       Map data = {
  4.    'name': 'jack'};
  5.       BoostChannel.instance.sendEventToNative('updateUser', data);
  6.     },
  7.     child: const Text('发送消息'),
  8.   ),
复制代码
    鸿蒙侧
pages/FlutterPage.ets
  1. aboutToAppear() {
  2.   this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams())
  3.   this.flutterEntry?.aboutToAppear()
  4.   this.flutterView = this.flutterEntry?.getFlutterView()
  5.   const plugin = FlutterBoost.getInstance()
  6.     .getPlugin()
  7.   if (plugin) {
  8.     // 通信
  9.     plugin.addEventListener('updateUser', {
  10.       onEvent: (key, args) => {
  11.         // logger.debug(`事件名称 ${key}`, JSON.stringify(args))
  12.         promptAction.showToast({
  13.     message: 'Flutter Data: ' + JSON.stringify(args) })
  14.       }
  15.     })
  16.   }
复制代码
Flutter 调用鸿蒙原生

    Flutter 侧
  1. final _platform = const MethodChannel('xyz.zhousg.interview_success_project');
复制代码
  1. TextButton(
  2.     onPressed: () {
  3.       _platform.invokeMethod('openCamera').then(
  4.             (value) => setState(() {
  5.               // value 鸿蒙侧回传数据
  6.             }),
  7.           );
  8.     },
  9.     child: Text('打开相机),
  10. ),
复制代码
    鸿蒙侧
定义 Flutter 插件
NativePlugin.ets
  1. import {
  2.     FlutterPlugin, FlutterPluginBinding, MethodChannel, MethodResult } from '@ohos/flutter_ohos';
  3. export class NativePlugin implements FlutterPlugin {
  4.   private channel?: MethodChannel;
  5.   getUniqueClassName(): string {
  6.     return 'CameraPlugin'
  7.   }
  8.   onAttachedToEngine(binding: FlutterPluginBinding): void {
  9.     this.channel = new MethodChannel(binding.getBinaryMessenger(), 'xyz.zhousg.interview_success_project')
  10.     this.channel.setMethodCallHandler({
  11.       onMethodCall: (call, result) => {
  12.         switch (call.method) {
  13.           case "openCamera":
  14.             this.openCamera(result)
  15.             break;
  16.           default:
  17.             result.notImplemented()
  18.             break;
  19.         }
  20.       }
  21.     })
  22.   }
  23.   onDetachedFromEngine(binding: FlutterPluginBinding): void {
  24.     this.channel?.setMethodCallHandler(null);
  25.   }
  26.   // native api
  27.   openCamera (result: MethodResult) {
  28.     // 回传数据给 Flutter
  29.     result.success('http://test.png')
  30.   }
  31. }
复制代码
注册插件
entryAbility.ets
  1. // Initial FlutterBoost
  2. const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()
  3. FlutterBoost.getInstance().setup(this, this.context, (engine) => {
  4.   GeneratedPluginRegistrant.registerWith(engine)
  5.   // 打开相机
  6.   engine.getPlugins()?.add(new NativePlugin())
  7. }, optionsBuilder.build())
复制代码
总结

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

本版积分规则

快速回复 返回顶部 返回列表