有时我们需要借助原生平台的强大功能来实现更复杂、更具特色的交互效果,对于鸿蒙系统而言,如何在Flutter应用里嵌入鸿蒙原生视图并实现两者的双向通信呢?这就是本文要探讨的内容。

一、为什么要在Flutter中使用鸿蒙原生视图

在跨平台开发的大环境下,Flutter提供了一种便捷的方式来构建多平台应用。然而,每个平台都有其独特的特性和优势。为了充分发挥鸿蒙系统的潜力,让Flutter应用在鸿蒙设备上拥有更原生的体验,通过特定机制嵌入鸿蒙原生视图就显得尤为重要。

Flutter的PlatformView机制,为开发者提供了嵌入原生平台视图组件的能力。在鸿蒙系统中,我们也可以利用类似的方法,实现Flutter与鸿蒙原生组件的融合,让应用兼具Flutter的跨平台优势和鸿蒙原生组件的特性。

二、PlatformView机制详解

Flutter的PlatformView机制就像是一座桥梁,它能够把原生视图渲染到Flutter的组件树中。在鸿蒙系统下运用这个机制,主要涉及以下几个关键步骤:

  1. 创建鸿蒙原生组件:我们需要使用Java或者JS语言来编写鸿蒙系统的用户界面组件。这些组件就是后续要嵌入到Flutter应用中的原生部分。
  2. 建立通信桥梁:通过MethodChannel,可以实现Flutter与原生端之间的双向通信。这就好比在两个不同的世界之间搭建了一条信息高速公路,让数据能够在Flutter和鸿蒙原生组件之间自由传输。
  3. 处理布局与生命周期:要确保Flutter与原生视图在布局和渲染上协调一致,避免出现显示异常或者性能问题,让用户有流畅的使用体验。

三、实战步骤:一步步实现Flutter与鸿蒙原生视图交互

(一)创建Flutter插件项目

首先,我们要创建一个Flutter插件项目,为后续的开发搭建基础框架。在命令行中执行以下命令:

flutter create --template=plugin harmony_view 

这个命令会创建一个名为harmony_view的Flutter插件项目,后续的代码开发都将在这个项目中进行。

(二)鸿蒙原生端实现

使用DevEco Studio工具打开harmony_viewohos项目,接下来在harmony_viewohosentrysrcmainetsentryability目录下开展一系列的代码编写工作。

  1. 新建自定义视图文件:新建CustomView.ets文件,这个文件里定义的CustomView是要在Flutter组件中显示的内容。
@Component struct ButtonComponent { @Prop params: Params customView: CustomView = this.params.platformView as CustomView @StorageLink('numValue') storageLink: string = "first" @State bkColor: Color = Color.Red build() { Column() { Button("发送数据给Flutter") .border({ width: 2, color: Color.Blue}) .backgroundColor(this.bkColor) .onTouch((event: TouchEvent) => { console.log("nodeController button on touched") }) .onClick((event: ClickEvent) => { this.customView.sendMessage(); console.log("nodeController button on click") }) Text(`来自Flutter的数据 : ${this.storageLink}`) .onTouch((event: TouchEvent) => { console.log("nodeController text on touched") }) }.alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) .direction(Direction.Ltr) .width('100%') .height('100%') } } 

在这段代码中,定义了一个包含按钮和文本的组件。按钮点击时会调用customView.sendMessage()方法向Flutter发送数据,文本则用于显示从Flutter接收的数据。
2. 定义组件构建方法:定义一个builder方法,将上面的自定义Component组件放入其中。

@Builder function ButtonBuilder(params: Params) { ButtonComponent({ params: params }) .backgroundColor(Color.Yellow) } 

这个ButtonBuilder方法用于构建ButtonComponent组件,并设置其背景颜色为黄色。
3. 继承并实现自定义视图:继承PlatformView实现一个自定义的Customview,实现getView接口,返回WrappedBuilder(ButtonBuilder)

import MethodChannel, { MethodCallHandler, MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel'; import PlatformView, { Params } from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView'; import common from '@ohos.app.ability.common'; import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; import StandardMethodCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMethodCodec'; import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall'; @Observed export class CustomView extends PlatformView implements MethodCallHandler { numValue: string = "test"; methodChannel: MethodChannel; index: number = 1; constructor(context: common.Context, viewId: number, args: ESObject, message: BinaryMessenger) { super(); console.log("nodeController viewId:" + viewId) // 注册消息通道,消息通道根据具体需求添加,代码仅作为示例 this.methodChannel = new MethodChannel(message, `com.rex.custom.ohos/customView${viewId}`, StandardMethodCodec.INSTANCE); this.methodChannel.setMethodCallHandler(this); } onMethodCall(call: MethodCall, result: MethodResult): void { // 接受Dart侧发来的消息 let method: string = call.method; let link1: SubscribedAbstractProperty<number> = AppStorage.link('numValue'); switch (method) { case 'getMessageFromFlutterView': let value: ESObject = call.args; this.numValue = value; link1.set(value) console.log("nodeController receive message from dart: " + this.numValue); result.success(true); break; } } public sendMessage = () => { console.log("nodeController sendMessage") //向Dart侧发送消息 this.methodChannel.invokeMethod('getMessageFromOhosView', 'natvie - ' + this.index++); } getView(): WrappedBuilder<[Params]> { return new WrappedBuilder(ButtonBuilder); } dispose(): void { } } 

这段代码实现了与Flutter的双向通信。在构造函数中注册了消息通道,onMethodCall方法用于接收Flutter发送的消息,sendMessage方法则用于向Flutter发送消息。getView方法返回构建好的视图。
4. 实现自定义视图工厂:实现一个自定义的PlatformViewFactory,在其create方法中创建自定义的PlatformView实例。

import common from '@ohos.app.ability.common'; import MessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/MessageCodec'; import PlatformViewFactory from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformViewFactory'; import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger'; import PlatformView from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView'; import { CustomView } from './CustomView'; export class CustomFactory extends PlatformViewFactory { message: BinaryMessenger; constructor(message: BinaryMessenger, createArgsCodes: MessageCodec<Object>) { super(createArgsCodes); this.message = message; } public create(context: common.Context, viewId: number, args: Object): PlatformView { return new CustomView(context, viewId, args, this.message); } } 

CustomFactory负责创建CustomView实例,在create方法中传入相关参数进行实例化。
5. 注册自定义插件:新建一个继承于FlutterPluginCustomPlugin插件,在onAttachedToEngine中注册自定义的PlatformViewFactory

import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin'; import StandardMessageCodec from '@ohos/flutter_ohos/src/main/ets/plugin/common/StandardMessageCodec'; import { CustomFactory } from './CustomFactory'; export class CustomPlugin implements FlutterPlugin { getUniqueClassName(): string { return 'CustomPlugin'; } onAttachedToEngine(binding: FlutterPluginBinding): void { binding.getPlatformViewRegistry()?. registerViewFactory('com.rex.custom.ohos/customView', new CustomFactory(binding.getBinaryMessenger(), StandardMessageCodec.INSTANCE)); } onDetachedFromEngine(binding: FlutterPluginBinding): void {} } 

通过CustomPlugin,将CustomFactory注册到Flutter插件引擎中,使得Flutter能够识别并使用我们自定义的PlatformView

(三)Flutter端实现

使用AndroidStudio工具打开harmony_view项目,在harmony_viewlib目录下进行代码编写。

  1. 实现自定义视图Widget:新建CustomOhosView.dart文件,用于显示原生侧的CustomView组件。OhosView是桥接PlatformView的关键。
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; typedef OnViewCreated = Function(CustomViewController); ///自定义OhosView class CustomOhosView extends StatefulWidget { final OnViewCreated onViewCreated; const CustomOhosView(this.onViewCreated, {Key? key}) : super(key: key); @override State<CustomOhosView> createState() => _CustomOhosViewState(); } class _CustomOhosViewState extends State<CustomOhosView> { late MethodChannel _channel; @override Widget build(BuildContext context) { return _getPlatformFaceView(); } Widget _getPlatformFaceView() { return OhosView( viewType: 'com.rex.custom.ohos/customView', onPlatformViewCreated: _onPlatformViewCreated, creationParams: const <String, dynamic>{'initParams': 'hello world'}, creationParamsCodec: const StandardMessageCodec(), ); } void _onPlatformViewCreated(int id) { _channel = MethodChannel('com.rex.custom.ohos/customView$id'); final controller = CustomViewController._( _channel, ); widget.onViewCreated(controller); } } 

在这段代码中,CustomOhosView通过OhosView来加载原生视图。viewType要与鸿蒙原生端注册的viewType一致,onPlatformViewCreated回调用于在原生视图创建成功后进行一些初始化操作。
2. 实现交互控制类:在CustomOhosView所在文件中新建CustomViewController,用于实现Dart侧与原生侧的交互。

class CustomViewController { final MethodChannel _channel; final StreamController<String> _controller = StreamController<String>(); CustomViewController._( this._channel, ) { _channel.setMethodCallHandler( (call) async { switch (call.method) { case 'getMessageFromOhosView': // 从native端获取数据 final result = call.arguments as String; _controller.sink.add(result); break; } }, ); } Stream<String> get customDataStream => _controller.stream; // 发送数据给native Future<void> sendMessageToOhosView(String message) async { await _channel.invokeMethod( 'getMessageFromFlutterView', message, ); } } 

CustomViewController通过MethodChannel与原生端进行通信。setMethodCallHandler用于接收原生端发送的消息,sendMessageToOhosView方法则用于向原生端发送消息。
3. 编写测试代码:修改harmony_viewlibmain.dart文件中的代码进行测试。

import 'dart:math'; import 'package:flutter/material.dart'; import 'custom_ohos_view.dart'; void main() { runApp(const MaterialApp(home: MyHome())); } class MyHome extends StatelessWidget { const MyHome({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const Scaffold( body: CustomExample(), ); } } class CustomExample extends StatefulWidget { const CustomExample({Key? key}) : super(key: key); @override State<CustomExample> createState() => _CustomExampleState(); } class _CustomExampleState extends State<CustomExample> { String receivedData = ''; CustomViewController? _controller; void _onCustomOhosViewCreated(CustomViewController controller) { _controller = controller; _controller?.customDataStream.listen((data) { //接收到来自OHOS端的数据 setState(() { receivedData = '来自ohos的数据:$data'; }); }); } Widget _buildOhosView() { return Expanded( child: Container( color: Colors.blueAccent.withAlpha(60), child: CustomOhosView(_onCustomOhosViewCreated), ), flex: 1, ); } Widget _buildFlutterView() { return Expanded( child: Stack( alignment: AlignmentDirectional.bottomCenter, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ TextButton( onPressed: () { final randomNum = Random().nextInt(10); _controller ?.sendMessageToOhosView('flutter - $randomNum '); }, child: const Text('发送数据给ohos'), ), const SizedBox(height: 10), Text(receivedData), ], ), const Padding( padding: EdgeInsets.only(bottom: 15), child: Text( 'Flutter - View', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), ), ], ), flex: 1, ); } @override Widget build(BuildContext context) { return Column( children: [ _buildOhosView(), _buildFlutterView(), ], ); } } 

main.dart中,构建了包含原生视图和Flutter视图的界面。通过按钮点击可以向鸿蒙原生端发送数据,同时接收并显示原生端返回的数据。

四、总结

通过PlatformView机制在Flutter中集成鸿蒙原生视图,我们既能保留Flutter的跨平台开发优势,又能充分利用鸿蒙系统的特性,实现渐进式的混合开发。随着鸿蒙生态的不断发展和完善,这种混合开发模式将为应用带来更强大的功能扩展能力。