鸿蒙-flutter-使用PlatformView

我们自己的业务比较简单,基本上没有使用PlatformView,所有的页面要么是原生,要么是flutter,没有这种在flutter页面上展示原生控件的需求。 这里介绍一下如何在纯flutter项目中使用platformView展示鸿蒙组件。

准备

按照之前的环境搭建和第一个helloworld,搭建好环境,运行起来。

原生侧

使用DevEco打开项目工程下的ohos文件夹,DevEco会将该文件夹识别为一个鸿蒙项目,可以获得完整的代码提示和语法高亮。 我们先从底层向接口方向编写代码。

需要展示的View

定义一个用来在Flutter上展示的 Component。

TypeScript 复制代码
import { CustomView } from "./CustomView" //这里的CustomView是我们后面需要继承PlatformView的类
import { Params } from '@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView';
@Component
export 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%')
  }
}

PlatformView的编写

我们需要继承PlatformView,并且实现其中的getView方法,返回一个WrappedBuilder, 在这个WrappedBuilder中,返回我们上面自定义的ButtonComponent。 当然免不了互相传输数据,因此我们这里还需要实现MethodCallHandler接口.

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';
import { ButtonComponent } from './ButtonComponent';

@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.huangyuanlove/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 {
  }
}
@Builder
export function ButtonBuilder(params: Params) {
  ButtonComponent({ params: params })
    .backgroundColor(Color.Yellow)
}

自定义PlatformViewFactory

在这里需要在其create方法中创建自定义的PlatformView的实例。这个PlatformViewFactory主要就干这件事情。

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);
  }
}

自定义FlutterPlugin

这里我们需要自定义一个继承于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.huangyuanlove/customView', new CustomFactory(binding.getBinaryMessenger(), StandardMessageCodec.INSTANCE));
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {}
}

添加Plugin

现在我们需要将上面自定义的plugin在EntryAbility中注册一下.

TypeScript 复制代码
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { CustomPlugin } from '../widget/CustomPlugin';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new CustomPlugin());
  }
}

至此,我们完成了原生侧的开发,下面看一下flutter侧怎么搞

Flutter侧

用于发送和接收数据的Controller

这里我们先封装一个用于和原生侧进行数据交互的类,就叫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,
    );
  }
}

这个类不封装也行,看自己的喜好。

用于展示原生控件的Widget

flutter侧比较简单,只需要搞一个用来展示原生控件的Widget就可以了,交互的话还是走channel,就是用上面封装的CustomViewController.

dart 复制代码
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.huangyuanlove/customView',
      onPlatformViewCreated: _onPlatformViewCreated,
      creationParams: const <String, dynamic>{'initParams': 'hello world'},
      creationParamsCodec: const StandardMessageCodec(),
    );
  }

  void _onPlatformViewCreated(int id) {
    _channel = MethodChannel('com.huangyuanlove/customView$id');
    final controller = CustomViewController._(
      _channel,
    );
    widget.onViewCreated(controller);
  }
}

这里的OhosView组件就是用来桥接PlatformView组件的。 其中:

viewType:传递给Native侧,告知插件需要创建那个PlatformView,这个PlatformView需要在插件初始化时注册。 onPlatformViewCreated:PlatformView创建成功时的回调。 creationParams:传递给PlatformView的初始化参数。

这里需要注意,参数viewType必须和原生侧的CustomPlugin类中的onAttachedToEngine方法中,调用registerViewFactory方法第一个参数一致。 在_onPlatformViewCreated方法中注册的Channel就更不需要多说了.

展示并运行

我们找个页面来同时展示一下flutter组件和原生组件,这里为了简单,直接修改了main.dart

dart 复制代码
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:ohflutter_3221/widget/CustomOhosView.dart';

void main() {
  runApp(const MaterialApp(home: Main()));
}

class Main extends StatelessWidget {
  const Main({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: CustomViewExample(),
    );
  }
}

class CustomViewExample extends StatefulWidget {
  const CustomViewExample({super.key});

  @override
  State<CustomViewExample> createState() => _CustomViewExampleState();
}

class _CustomViewExampleState extends State<CustomViewExample> {
  String receivedData = '';
  CustomViewController? _controller;

  void _onCustomOhosViewCreated(CustomViewController controller) {
    _controller = controller;
    _controller?.customDataStream.listen((data) {
      //接收到来自OHOS端的数据
      setState(() {
        receivedData = '来自ohos的数据:$data';
      });
    });
  }

  Widget _buildOhosView() {
    return Expanded(
      flex: 1,
      child: Container(
        color: Colors.blueAccent.withAlpha(60),
        child: CustomOhosView(_onCustomOhosViewCreated),
      ),
    );
  }

  Widget _buildFlutterView() {
    return Expanded(
      flex: 1,
      child: 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),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildOhosView(),
        _buildFlutterView(),
      ],
    );
  }
}

这样我们就完成了原生组件的展示,和flutter组件的通信。

效果

放个效果图\

相关推荐
zacksleo1 小时前
哪些鸿蒙原生应用在使用Flutter
前端·flutter·harmonyos
二流小码农1 小时前
鸿蒙开发:简单实现一个服务卡片
harmonyos
vvilkim2 小时前
Flutter布局系统全面解析:从基础组件到复杂界面构建
flutter
移动端开发者3 小时前
鸿蒙Next数据面板组件DataPanel介绍
harmonyos
移动端开发者3 小时前
鸿蒙Next使用Canvas绘制一个汽车仪表盘
harmonyos
移动端开发者3 小时前
鸿蒙Next数据量环形图标Gauge介绍
harmonyos
塞尔维亚大汉3 小时前
鸿蒙开发面试真题:鸿蒙操作系统的微内核架构有哪些优势?
面试·harmonyos
程序员老刘3 小时前
MCP:新时代的API,每个程序员都应该掌握
人工智能·flutter·mcp
恋猫de小郭3 小时前
Flutter 小技巧之:实现 iOS 26 的 “液态玻璃”
android·前端·flutter
帅次3 小时前
Flutter Container 组件详解
android·flutter·ios·小程序·kotlin·iphone·xcode