章
目
录
有时我们需要借助原生平台的强大功能来实现更复杂、更具特色的交互效果,对于鸿蒙系统而言,如何在Flutter应用里嵌入鸿蒙原生视图并实现两者的双向通信呢?这就是本文要探讨的内容。
一、为什么要在Flutter中使用鸿蒙原生视图
在跨平台开发的大环境下,Flutter提供了一种便捷的方式来构建多平台应用。然而,每个平台都有其独特的特性和优势。为了充分发挥鸿蒙系统的潜力,让Flutter应用在鸿蒙设备上拥有更原生的体验,通过特定机制嵌入鸿蒙原生视图就显得尤为重要。
Flutter的PlatformView
机制,为开发者提供了嵌入原生平台视图组件的能力。在鸿蒙系统中,我们也可以利用类似的方法,实现Flutter与鸿蒙原生组件的融合,让应用兼具Flutter的跨平台优势和鸿蒙原生组件的特性。
二、PlatformView机制详解
Flutter的PlatformView
机制就像是一座桥梁,它能够把原生视图渲染到Flutter的组件树中。在鸿蒙系统下运用这个机制,主要涉及以下几个关键步骤:
- 创建鸿蒙原生组件:我们需要使用Java或者JS语言来编写鸿蒙系统的用户界面组件。这些组件就是后续要嵌入到Flutter应用中的原生部分。
- 建立通信桥梁:通过
MethodChannel
,可以实现Flutter与原生端之间的双向通信。这就好比在两个不同的世界之间搭建了一条信息高速公路,让数据能够在Flutter和鸿蒙原生组件之间自由传输。 - 处理布局与生命周期:要确保Flutter与原生视图在布局和渲染上协调一致,避免出现显示异常或者性能问题,让用户有流畅的使用体验。
三、实战步骤:一步步实现Flutter与鸿蒙原生视图交互
(一)创建Flutter插件项目
首先,我们要创建一个Flutter插件项目,为后续的开发搭建基础框架。在命令行中执行以下命令:
flutter create --template=plugin harmony_view
这个命令会创建一个名为harmony_view
的Flutter插件项目,后续的代码开发都将在这个项目中进行。
(二)鸿蒙原生端实现
使用DevEco Studio
工具打开harmony_view\ohos
项目,接下来在harmony_view\ohos\entry\src\main\ets\entryability
目录下开展一系列的代码编写工作。
- 新建自定义视图文件:新建
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. 注册自定义插件:新建一个继承于FlutterPlugin
的CustomPlugin
插件,在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_view\lib
目录下进行代码编写。
- 实现自定义视图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_view\lib\main.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的跨平台开发优势,又能充分利用鸿蒙系统的特性,实现渐进式的混合开发。随着鸿蒙生态的不断发展和完善,这种混合开发模式将为应用带来更强大的功能扩展能力。