一、背景介绍
在跨平台开发中,Flutter 通过 PlatformView
机制允许开发者嵌入原生平台的视图组件。对于鸿蒙(HarmonyOS)系统,同样可以通过类似机制实现 Flutter 与原生组件的无缝整合。本文将介绍如何在 Flutter 应用中嵌入鸿蒙原生视图,并实现双向通信。
二、 PlatformView 机制
Flutter 通过 PlatformView
接口将原生视图渲染到 Flutter 的 widget 树中。对于鸿蒙系统,我们需要:
1、创建鸿蒙原生组件:使用 Java/JS 编写 HarmonyOS 的 UI 组件。
2、建立通信桥梁:通过 MethodChannel
实现 Flutter 与原生端的双向通信。
2、处理布局与生命周期:协调 Flutter 与原生视图的布局和渲染。
三、实现步骤
1、创建 Flutter 插件项目:
flutter create --template=plugin harmony_view
2、鸿蒙原生端实现
使用 DevEco Studio
工具打开 harmony_view\ohos
项目,在harmony_view\ohos\entry\src\main\ets\entryability
目录下实现代码
2.1、新建CustomView.ets
文件,CustomView
用于在Flutter Widget里显示
less
@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%')
}
}
2.2、定义一个builder方法,放入2.1的自定义Component组件
scss
@Builder
function ButtonBuilder(params: Params) {
ButtonComponent({ params: params })
.backgroundColor(Color.Yellow)
}
2.3、继承PlatformView实现一个自定义的Customview,实现getView接口,返回WrappedBuilder(ButtonBuilder)
,放入2.2的的builder方法
typescript
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 {
}
}
2.4、实现一个自定义的PlatformViewFactory
,在其create
方法中创建自定义的PlatformView
的实例
typescript
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);
}
}
1.5、新建一个继承于FlutterPlugin
的CustomPlugin插件,在onAttachedToEngine
中,注册自定义的PlatformViewFactory
typescript
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 {}
}
3、Flutter 端实现
用AndroidStudio
工具打开harmony_view
项目在harmony_view\lib
目录下实现代码,新建CustomOhosView.dart
,用于显示Native
侧的CustomView的Widget
OhosView组件就是桥接PlatformView的关键。
- viewType:传递给Native侧,告知插件需要创建那个PlatformView,这个PlatformView需要在插件初始化时注册。
- onPlatformViewCreated:PlatformView创建成功时的回调。
- creationParams:传递给PlatformView的初始化参数。3.1、实现CustomOhosView,使用OhosView组件,viewType需要和ets侧FlutterPlugin做registerViewFactory操作时指定的viewType一致
3.1、实现CustomOhosView,使用OhosView组件,viewType需要和ets侧FlutterPlugin做registerViewFactory操作时指定的viewType一致
scala
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);
}
}
3.2、在CustomOhosView
所在文件中新建CustomViewController
,用于实现Dart侧与Native侧的交互
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,
);
}
}
3.3、修改harmony_view\lib\main.dart
文件中的代码写测试
php
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(),
],
);
}
}
四、总结
通过 PlatformView 集成鸿蒙原生视图,开发者可以:
- 保留 Flutter 跨平台优势
- 充分利用鸿蒙系统特性
- 实现渐进式混合开发
未来随着 HarmonyOS 生态的发展,这种混合开发模式将显著提升应用的功能扩展能力。