HarmonyOS NEXT-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 项目

bash 复制代码
flutter create -t module --org xyz.zhousg demo_fluter 

打包 Fluter 项目

bash 复制代码
flutter build har --debug

Fluter 引入 flutter_boost

  • 安装依赖
diff 复制代码
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'
  • 配置路由表
dart 复制代码
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<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)!;
  }

  @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 项目,会生成三个产物

text 复制代码
.ohos
   |--har
       |-- fluter_boost.har
       |-- fluter_module.har
       |-- fluter.har

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

oh-package.json5

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

TS 复制代码
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

ts 复制代码
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

ts 复制代码
// 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侧
dart 复制代码
  TextButton(
    onPressed: () {
      Map<String, String> data = {'name': 'jack'};
      BoostChannel.instance.sendEventToNative('updateUser', data);
    },
    child: const Text('发送消息'),
  ),
  • 鸿蒙侧

pages/FlutterPage.ets

ts 复制代码
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侧
dart 复制代码
final _platform = const MethodChannel('xyz.zhousg.interview_success_project');
dart 复制代码
TextButton(
    onPressed: () {
      _platform.invokeMethod('openCamera').then(
            (value) => setState(() {
              // value 鸿蒙侧回传数据
            }),
          );
    },
    child: Text('打开相机),
),
  • 鸿蒙侧

定义 Flutter 插件

NativePlugin.ets

ts 复制代码
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

ts 复制代码
// 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页面栈+鸿蒙页面栈跳转,搞定数据通信和原生调用,基本可以满足一般的开发,以上这里仅供参考哈~

相关推荐
坚果的博客11 分钟前
鸿蒙版Flutter快递查询助手
flutter·华为·harmonyos
轻口味13 分钟前
【每日学点HarmonyOS Next知识】状态栏控制、片段按钮点击回调、绘制组件、取消按钮与输入框对齐、父调子组件方法
pytorch·华为·harmonyos·harmonyosnext
花先锋队长15 分钟前
鸿蒙生态日日新,夸克、顺丰速运、驾校一点通等多款应用功能更新
华为·harmonyos
zzialx16 分钟前
HarmonyOS:基于hmrouter实现Page的生命周期监听
华为·harmonyos
别说我什么都不会1 小时前
当OpenHarmony遇上OpenEuler
操作系统·嵌入式·harmonyos
没有了遇见2 小时前
鸿蒙学习ArkTS之工程目录说
harmonyos
没有了遇见2 小时前
鸿蒙学习ArkTS之Module创建和引用
harmonyos
晴天学长2 小时前
一个多功能的GetX 项目代码生成工具
前端·flutter
蜡笔小新..2 小时前
Windows下配置Flutter移动开发环境以及AndroidStudio安装和模拟机配置
windows·flutter