Flutter鸿蒙下原生视图的使用说明

一、背景介绍

在跨平台开发中,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 生态的发展,这种混合开发模式将显著提升应用的功能扩展能力。

相关推荐
mldong11 分钟前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排1 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60611 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角2 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰3 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端