|
在 Flutter 三端分离模式下完成纯血鸿蒙混入的过程中,虽然官方文档提供了一定的指导,但实际操作中可能会遇到一些坑。以下是我在适配过程中的一些经验总结,供各位开发者参考 😄 如果有帮助点个赞。
在混入过程中是基于咸鱼团队 flutter_boost(这里不讨论和其他方案的差别) 和自定义 FlutterPlugin 实现的。
主要涉及内容:
环境搭建Flutter module 创建Futter 引入 flutter_boostHarmony 引入 flutter_boostFlutter 与鸿蒙侧通信Flutter 调用鸿蒙原生
环境搭建
准备支持鸿蒙的 Flutter 开发环境,flutter_fluter 仓库基于 Flutter SDK 对于 OpenHarmony 平台的兼容拓展,可支持 IDE 或者终端使用 Flutter Tools 指令编译和构建 OpenHarmony 应用程序。
不再赘述,上链接。
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});
- @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%')
- }
- }
复制代码 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 与鸿蒙侧通信
- 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 调用鸿蒙原生
- 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 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 页面栈 + 鸿蒙页面栈跳转,搞定数据通信和原生调用,基本可以满足一般的开发,以上这里仅供参考哈~ |
|