Flutter与鸿蒙原生视图交互原理与实战

前端 潘老师 2周前 (04-09) 18 ℃ (0) 扫码查看

有时我们需要借助原生平台的强大功能来实现更复杂、更具特色的交互效果,对于鸿蒙系统而言,如何在Flutter应用里嵌入鸿蒙原生视图并实现两者的双向通信呢?这就是本文要探讨的内容。

一、为什么要在Flutter中使用鸿蒙原生视图

在跨平台开发的大环境下,Flutter提供了一种便捷的方式来构建多平台应用。然而,每个平台都有其独特的特性和优势。为了充分发挥鸿蒙系统的潜力,让Flutter应用在鸿蒙设备上拥有更原生的体验,通过特定机制嵌入鸿蒙原生视图就显得尤为重要。

Flutter的PlatformView机制,为开发者提供了嵌入原生平台视图组件的能力。在鸿蒙系统中,我们也可以利用类似的方法,实现Flutter与鸿蒙原生组件的融合,让应用兼具Flutter的跨平台优势和鸿蒙原生组件的特性。

二、PlatformView机制详解

Flutter的PlatformView机制就像是一座桥梁,它能够把原生视图渲染到Flutter的组件树中。在鸿蒙系统下运用这个机制,主要涉及以下几个关键步骤:

  1. 创建鸿蒙原生组件:我们需要使用Java或者JS语言来编写鸿蒙系统的用户界面组件。这些组件就是后续要嵌入到Flutter应用中的原生部分。
  2. 建立通信桥梁:通过MethodChannel,可以实现Flutter与原生端之间的双向通信。这就好比在两个不同的世界之间搭建了一条信息高速公路,让数据能够在Flutter和鸿蒙原生组件之间自由传输。
  3. 处理布局与生命周期:要确保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目录下开展一系列的代码编写工作。

  1. 新建自定义视图文件:新建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. 注册自定义插件:新建一个继承于FlutterPluginCustomPlugin插件,在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目录下进行代码编写。

  1. 实现自定义视图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的跨平台开发优势,又能充分利用鸿蒙系统的特性,实现渐进式的混合开发。随着鸿蒙生态的不断发展和完善,这种混合开发模式将为应用带来更强大的功能扩展能力。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/17040.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】