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

相关推荐
前端大卫29 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘1 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare1 小时前
浅浅看一下设计模式
前端
Lee川1 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人2 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼2 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端