鸿蒙原生与 Flutter 混合开发:三种通讯方式快速上手

鸿蒙原生与 Flutter 混合开发:三种通讯方式快速上手

这篇文档对应华为开发者官网的混合开发说明,重点讲鸿蒙原生(ArkTS)和 Flutter 之间怎么互相传数据、互相调用。我们把它拆成最常用的三种通道,每种都配一段能直接照着写的代码。

文中的代码不是演示用的假代码,而是已经在下面的项目里编译通过的写法:


一、先搞清楚一件事:谁找谁

混合开发里,Flutter 页面和鸿蒙原生页面放在同一个 App 里。它们互相不认识,所以需要一条“通道”来传话。

华为文档里说的三种通道,本质区别就一句话:数据是谁发起、流向哪边、能不能收到回复

通道 谁发起 数据方向 有没有回复 典型场景
MethodChannel Flutter 主动调用原生 Flutter → 原生 → Flutter 有,一次一答 获取设备号、调相机、算一个结果
BasicMessageChannel 双方都可以主动发 双向 可以回复,也可以不回复 传简单字符串、JSON、自定义协议
EventChannel 原生主动推给 Flutter 原生 → Flutter 没有,持续发事件流 传感器数据、电量变化、进度更新

一句话记忆:

  • 想“问一句、答一句”,用 MethodChannel
  • 想“来回聊天”,用 BasicMessageChannel
  • 想“原生不停往 Flutter 推送”,用 EventChannel

注意:三种通道的名字就是它们的“身份证”,Flutter 侧和原生侧必须写成一模一样,否则对不上。


二、MethodChannel:一问一答

2.1 什么时候用

Flutter 想调用原生能力,并且要拿到一个返回值。比如:

  • 获取设备的某个信息;
  • 调用原生已经写好的功能;
  • 让原生帮忙算个数再还回来。

华为官网的用例是“获取设备号”,我们项目里对应的是“减法由原生计算后返回”。

2.2 数据怎么走

Flutter 调 invokeMethod('subtract')
        ↓
鸿蒙原生收到方法名和参数
        ↓
原生计算(执行的函数),result.success(结果)
        ↓
Flutter 拿到结果并显示

2.3 Flutter 侧代码

import 'package:flutter/services.dart';

// 通道名,两边必须一致
const MethodChannel _nativeChannel =
    MethodChannel('com.example.hybrid/native_calculator');

Future<void> subtract() async {
  // 把两个数传给原生,原生算完把差返回来
  final result = await _nativeChannel.invokeMethod<dynamic>(
    'subtract',
    {'first': 12, 'second': 5},
  );
  print(result); // 7
}

2.4 鸿蒙原生侧代码

import { MethodChannel } from '@ohos/flutter_ohos';

const channel = new MethodChannel(
  binaryMessenger,
  'com.example.hybrid/native_calculator'
);

channel.setMethodCallHandler({
  onMethodCall: (call, result) => {
    if (call.method === 'subtract') {
      const first = call.argument('first') as number;
      const second = call.argument('second') as number;
      // 计算在原生侧完成
      result.success(first - second);
      return;
    }
    result.notImplemented();
  }
});

三、MethodChannel 反着用:原生调 Flutter

MethodChannel 不是只能 Flutter 先开口。原生也可以反过来调用 Flutter 里已经写好的方法。

3.1 Flutter 侧:先注册好一个方法等着被调

const MethodChannel _flutterChannel =
    MethodChannel('com.example.hybrid/flutter_calculator');

// 在 initState 里注册
_flutterChannel.setMethodCallHandler((call) async {
  if (call.method == 'divide') {
    return first / second;
  }
  throw PlatformException(code: 'NOT_IMPLEMENTED');
});

3.2 原生侧:主动调一下

const flutterChannel = new MethodChannel(
  binaryMessenger,
  'com.example.hybrid/flutter_calculator'
);

// 原生主动让 Flutter 执行除法
flutterChannel.invokeMethod('divide', null);

对应项目里的按钮:除法:原生调 Flutter


四、BasicMessageChannel:双向聊天

4.1 什么时候用

两边都要能主动发消息,数据是字符串、简单对象或 JSON。它比 MethodChannel 更“轻”,没有方法名,直接发一条消息,对方可以回复。

我们项目里对应“乘法”:Flutter 发 3,4 给原生,原生算完把 3 × 4 = 12 回给 Flutter。

4.2 数据怎么走

Flutter 发消息 "3,4"
        ↓
鸿蒙原生收到后处理
        ↓
原生 reply.reply("3 × 4 = 12")
        ↓
Flutter 收到回复

4.3 Flutter 侧代码

import 'package:flutter/services.dart';

// StringCodec 表示这条通道传的是字符串
const BasicMessageChannel<String> _msgChannel =
    BasicMessageChannel<String>(
      'com.example.hybrid/basic_message',
      StringCodec(),
    );

final reply = await _msgChannel.send('3,4');
print(reply); // 3 × 4 = 12

4.4 鸿蒙原生侧代码

import {
  BasicMessageChannel,
  StringCodec,
} from '@ohos/flutter_ohos';

const msgChannel = new BasicMessageChannel<string>(
  binaryMessenger,
  'com.example.hybrid/basic_message',
  StringCodec.INSTANCE
);

msgChannel.setMessageHandler({
  onMessage: (message, reply) => {
    const parts = message.split(',');
    const first = Number(parts[0]);
    const second = Number(parts[1]);
    // 原生把结果回给 Flutter
    reply.reply(`${first} × ${second} = ${first * second}`);
  }
});

4.5 原生也可以先开口

反过来,原生想主动发消息给 Flutter 时,Flutter 也要提前注册一个接收器:

const BasicMessageChannel<String> _nativeMsgChannel =
    BasicMessageChannel<String>(
      'com.example.hybrid/native_message',
      StringCodec(),
    );

_nativeMsgChannel.setMessageHandler((message) async {
  print('收到原生消息:$message');
  return '已收到';
});

原生侧直接发:

const nativeMsgChannel = new BasicMessageChannel<string>(
  binaryMessenger,
  'com.example.hybrid/native_message',
  StringCodec.INSTANCE
);

nativeMsgChannel.send('这是鸿蒙原生发来的消息');

五、EventChannel:原生持续推事件

5.1 什么时候用

数据主要从原生往 Flutter 流,而且可能不止一次。比如传感器读数、网络状态变化、进度更新。

我们项目里对应“原生推事件”:鸿蒙原生通过 EventChannel 推一条设备信息给 Flutter。

5.2 数据怎么走

Flutter 先订阅事件流
        ↓
鸿蒙原生保存 EventSink(一个“发送口”)
        ↓
原生随时 sink.success(事件)
        ↓
Flutter 的监听回调收到事件

5.3 Flutter 侧代码

import 'dart:async';
import 'package:flutter/services.dart';

const EventChannel _eventChannel =
    EventChannel('com.example.hybrid/native_event');

StreamSubscription? _subscription;

_subscription = _eventChannel
    .receiveBroadcastStream()
    .listen((event) {
      print('收到原生事件:$event');
    });

// 页面销毁时记得取消订阅
_subscription?.cancel();

5.4 鸿蒙原生侧代码

import { EventChannel, EventSink } from '@ohos/flutter_ohos';

private nativeEventSink: EventSink | null = null;

const eventChannel = new EventChannel(
  binaryMessenger,
  'com.example.hybrid/native_event'
);

eventChannel.setStreamHandler({
  onListen: (args, events) => {
    // Flutter 开始监听时,把发送口保存下来
    this.nativeEventSink = events;
  },
  onCancel: () => {
    // Flutter 取消监听时清掉
    this.nativeEventSink = null;
  }
});

// 以后任何地方、任何时候都能推一条事件
this.nativeEventSink?.success('设备号 3AQ0224B14026838');

六、华为官网用例和我们项目的对照

华为文档用“获取设备号”贯穿三种通道,我们的项目用“计算器”贯穿,逻辑是对应的:

华为文档 我们项目 通道
Flutter 调原生拿设备号 减法:Flutter 调原生算差 MethodChannel
原生方法由 Flutter 实现 除法:原生反过来调 Flutter MethodChannel 反向
双向发消息 乘法:Flutter 发两个数、原生回乘积 BasicMessageChannel
设备号变化持续推送 原生主动推事件给 Flutter EventChannel

七、最容易踩的几个坑

  1. 通道名对不上:Flutter 和原生必须用完全相同的字符串,少一个字符都会报 MissingPluginException

  2. 编解码器不匹配BasicMessageChannel 传字符串就用 StringCodec,两边要一致。

  3. 忘了取消订阅EventChannelStreamSubscription 在页面销毁时要 cancel(),否则会泄漏。

  4. 结果回调要各走各的路:原生处理完必须调用 result.successresult.errorresult.notImplemented 其中一个,不能什么都不做。

  5. 先注册再调用:如果原生要调 Flutter 的方法,Flutter 侧必须先把 setMethodCallHandler 注册好,否则原生调不到。

项目地址:https://gitee.com/wking123321/arkts-flutter

官网介绍:https://developer.huawei.com/consumer/cn/blog/topic/03223830707488374

posted @ 2026-09-09 15:19  带头大哥d小弟  阅读(4)  评论(0)    收藏  举报