本文首发于公众号:移动开发那些事:惊爆!Flutter消息通道的超神全解析!
在 Flutter 跨平台开发中,Dart 层 与原生层(Android/iOS)的通信是核心需求之一。无论是调用摄像头、麦克风等设备硬件能力,获取系统信息,还是处理高性能计算任务,均需通过消息通道实现数据交互。Flutter提供了三种核心消息通道,每种通道都有其独特的设计理念和适用场景。本文将详细解析这三种通道,并结合音频处理框架的设计,帮助开发者深化对各通道使用场景的理解。
2 消息通道类型详解
Flutter 的消息通道本质上是 Dart 层与原生层(Platform Channel)之间的通信桥梁,基于二进制流传输数据,通过编解码器(Codec)实现不同类型数据的序列化与反序列化。目前,Flutter主要提供了三种通道类型:BasicMessageChannel、MethodChannel 和 EventChannel。
2.1 BasicMessageChannel
BasicMessageChannel 用于Dart层与原生层之间的双向持续消息的传输,支持字符串、二进制、自定义对象等任意类型数据交互,适用于需频繁数据交换的场景。它依赖MessageCodec进行数据编码 / 解码,默认使用支持基础类型、列表、映射的 StandardMessageCodec,也可自定义 StringCodec、BinaryCodec 等编解码器
下面以一个简单的从Flutter层发送音频数据到原生层,原生层再把音频数据转成文本再发送回Flutter的例子来解释其的使用:
Flutter 端(Dart)- import 'package:flutter/services.dart';
- // 初始化BasicMessageChannel,指定通道名称和编解码器
- final BasicMessageChannel _audioChannel = BasicMessageChannel(
- 'com.example.audio_channel', // 通道唯一标识
- StandardMessageCodec(), // 默认编解码器
- );
- // 发送音频数据到原生层
- void sendAudioData(Uint8List audioBytes) async {
- try {
- // 发送音频数据到原生层
- _audioChannel.send(audioBytes);
- } on PlatformException catch (e) {
- print('发送失败:${e.message}');
- }
- }
- // 监听原生层主动发送的消息(如转录的文本)
- void setupAudioChannelListener() {
- _audioChannel.setMessageHandler((message) async {
- if (message is String) {
- print('收到转录结果:$message');
- }
- return ''; // 可选:向原生层返回确认
- });
- }
复制代码 原生层(Android - Kotlin)- import io.flutter.plugin.common.BasicMessageChannel
- import io.flutter.plugin.common.StandardMessageCodec
- import io.flutter.embedding.engine.FlutterEngine
- class AudioMessageHandler(flutterEngine: FlutterEngine) {
- // 初始化通道,与Flutter端通道名称保持一致
- private val audioChannel: BasicMessageChannel = BasicMessageChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.example.audio_channel", // 名字
- StandardMessageCodec.INSTANCE
- )
- init {
- // 设置消息处理器,接收Flutter发送的音频数据
- audioChannel.setMessageHandler { message, reply ->
- if (message is ByteArray) {
- // 模拟音频转写处理
- transcribeAudio(message)
- // 向Flutter返回转录结果
- reply.reply('')
- }
- }
- }
- // 模拟原生层主动发送进度信息到Flutter
- private fun sendTranscribeProgress(asrResult: String) {
- audioChannel.send(asrResult)
- }
- // 模拟音频转写逻辑
- private fun transcribeAudio(audioBytes: ByteArray) {
- // 实际场景中调用原生音频转写SDK
- sendTranscribeProgress("转写结果:这是一段测试音频")
- }
- }
复制代码 原生层(iOS - Swift)- import Flutter
- class AudioMessageHandler: NSObject, FlutterPlugin {
- private let audioChannel: FlutterBasicMessageChannel
-
- init(messenger: FlutterBinaryMessenger) {
- // 初始化通道
- audioChannel = FlutterBasicMessageChannel(
- name: "com.example.audio_channel",
- messenger: messenger,
- codec: FlutterStandardMessageCodec.sharedInstance()
- )
- super.init()
- setupHandler()
- }
-
- private func setupHandler() {
- // 处理Flutter发送的音频数据
- audioChannel.setMessageHandler { [weak self] message, reply in
- guard let audioData = message as? Data else {
- reply(nil)
- return
- }
- // 模拟音频转写
- self?.transcribeAudio(audioData)
- reply("")
- }
- }
-
- // 发送转写进度到Flutter
- private func sendTranscribeProgress(asrResult: String) {
- audioChannel.sendMessage(asrResult)
- }
-
- // 模拟音频转写
- private func transcribeAudio(_ data: Data) {
- self?.sendTranscribeProgress("转写结果:这是一段测试音频")
- }
- }
复制代码 上面只是一个最简单的使用BasicMessageChannel的示例,在实际的应用过程中, BasicMessageChannel的name 和 codec 一定要三个端都保持一致(Dart,Android,iOS)
此外,需注意一个实践细节:许多文档提及传输二进制数据时,使用 BinaryCodec 解码器效率最高,但实际测试发现,在 Android 平台中,BasicMessageChannel + BinaryCodec 存在特定 Bug—— 原生层发送至 Flutter 层的数据(无论原生层如何处理 ByteBuffer)始终为空(Flutter 层发送的数据可被原生层正常接收解析);相关讨论可参考 https://github.com/flutter/flutter/issues/19849
2.2 MethodChannel
MethodChannel 用于 Dart 层调用原生层方法(或原生层调用 Dart 层方法),支持同步与异步调用,适用于单次请求 - 响应式交互场景。它采用 “方法名 + 参数” 的通信模式,Dart 层通过 invokeMethod 调用原生方法,原生层则通过 MethodCallHandler 处理请求并返回结果。
MethodChannel支持的数据类型包括基本类型(boolean, int, double等)、字符串、列表和映射等,这些类型在消息中的序列化和反序列化会自动进行。同时,MethodChannel 具备全双工通信能力:Flutter 可主动向原生端发送消息并接收响应,原生端也可主动向 Flutter 端发送消息,待 Flutter 处理后接收返回结果。
简单的代码示例
Flutter 端(Dart)- import 'package:flutter/services.dart';
- // 初始化MethodChannel
- final MethodChannel _audioMethodChannel = MethodChannel('com.example.audio_method');
- // 调用原生层音频转写方法
- Future<String?> transcribeAudio(Uint8List audioBytes) async {
- try {
- // 调用原生方法“transcribe”,传入音频数据
- final result = await _audioMethodChannel.invokeMethod<String>(
- 'transcribe', // 方法名
- {'audioData': audioBytes}, // 参数(映射类型)
- );
- return result;
- } on PlatformException catch (e) {
- print('转写失败:${e.code} - ${e.message}');
- return null;
- }
- }
复制代码 原生层(Android - Kotlin)- import io.flutter.plugin.common.MethodChannel
- import io.flutter.embedding.engine.FlutterEngine
- class AudioMethodHandler(flutterEngine: FlutterEngine) {
- init {
- // 注册MethodChannel
- MethodChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.example.audio_method"
- ).setMethodCallHandler { call, result ->
- when (call.method) {
- "transcribe" -> {
- // 获取Flutter传入的音频数据
- val audioData = call.argument<ByteArray>("audioData")
- if (audioData == null) {
- result.error("INVALID_DATA", "音频数据为空", null)
- return@setMethodCallHandler
- }
- // 调用转写逻辑
- val transcript = transcribeAudio(audioData)
- result.success(transcript) // 返回结果
- }
- else -> {
- result.notImplemented() // 方法未实现
- }
- }
- }
- }
-
- private fun transcribeAudio(audioData: ByteArray): String {
- // 实际调用原生转写SDK
- return "转写结果:MethodChannel处理的音频"
- }
- }
复制代码 原生层(iOS - Swift)- import Flutter
- class AudioMethodHandler: NSObject, FlutterPlugin {
- static func register(with registrar: FlutterPluginRegistrar) {
- let channel = FlutterMethodChannel(
- name: "com.example.audio_method",
- binaryMessenger: registrar.messenger()
- )
- let instance = AudioMethodHandler()
- registrar.addMethodCallDelegate(instance, channel: channel)
- }
-
- func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
- switch call.method {
- case "transcribe":
- guard let args = call.arguments as? [String: Any],
- // 获取Flutter传入的音频数据
- let audioData = args["audioData"] as? Data else {
- result(FlutterError(code: "INVALID_DATA", message: "音频数据为空", details: nil))
- return
- }
- let transcript = transcribeAudio(audioData)
- result(transcript)
- default:
- result(FlutterMethodNotImplemented)
- }
- }
-
- private func transcribeAudio(_ data: Data) -> String {
- return "转写结果:MethodChannel处理的音频"
- }
- }
复制代码 2.3 EventChannel
EventChannel 专为持续的事件流或数据流通信而设计,主要用于原生层向Dart 层发送单向事件流(如传感器数据、实时日志).它的核心逻辑是:原生层通过 EventSink 发送 “成功”“错误”“结束” 等类型的事件,Dart 层则通过 Stream 监听事件流,自然适配连续数据的接收与处理。
,原生层通过EventSink发送事件(成功 / 错误 / 结束),Dart 层通过Stream监听事件流。
EventChannel 尤其适合处理原生平台产生的连续数据,例如传感器(加速度、陀螺仪)的实时数据、GPS 位置更新、实时音频流等。需注意的是,它是单向通信机制 —— 原生层可向 Flutter 端持续推送数据,但 Flutter 端无法通过同一通道向原生层回传数据。
简单的代码示例
Flutter 端(Dart)- import 'package:flutter/services.dart';
- // 初始化EventChannel
- final EventChannel _audioEventChannel = EventChannel('com.example.audio_events');
- // 监听原生层发送的转写事件流
- void listenToTranscribeEvents(Uint8List audioBytes) {
- // 获取事件流
- final stream = _audioEventChannel.receiveBroadcastStream(audioBytes);
-
- // 监听事件
- stream.listen(
- (event) {
- if (event is String) {
- print('收到转写片段:$event');
- } else if (event is double) {
- print('转写进度:${event * 100}%');
- }
- },
- onError: (error) {
- print('转写出错:$error');
- },
- onDone: () {
- print('转写完成');
- },
- );
- }
复制代码 原生层(Android - Kotlin)- import io.flutter.plugin.common.EventChannel
- import io.flutter.embedding.engine.FlutterEngine
- import kotlinx.coroutines.CoroutineScope
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.launch
- class AudioEventHandler(flutterEngine: FlutterEngine) {
- private var eventSink: EventChannel.EventSink? = null
-
- init {
- // 注册EventChannel
- EventChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.example.audio_events"
- ).setStreamHandler(object : EventChannel.StreamHandler {
- // 当Dart层开始监听时调用
- override fun onListen(arguments: Any?, sink: EventChannel.EventSink) {
- eventSink = sink
- if (arguments is ByteArray) {
- // 开始处理音频并发送事件
- processAudioStream(arguments)
- } else {
- sink.error("INVALID_ARG", "参数不是音频数据", null)
- }
- }
-
- // 当Dart层取消监听时调用
- override fun onCancel(arguments: Any?) {
- eventSink = null
- // 释放资源(如停止转写任务)
- }
- })
- }
-
- // 模拟流式转写(分片段发送结果)
- private fun processAudioStream(audioData: ByteArray) {
- // 这里需要实际调用其他服务来获取最终的结果
- CoroutineScope(Dispatchers.IO).launch {
- // 发送进度
- eventSink?.success(0.3)
- delay(500)
- // 发送转写片段
- eventSink?.success("这是第一段转写文本")
- delay(500)
- eventSink?.success(0.7)
- delay(500)
- eventSink?.success("这是第二段转写文本")
- delay(500)
- eventSink?.success(1.0)
- // 结束流
- eventSink?.endOfStream()
- }
- }
- }
复制代码 原生层(iOS - Swift)- import Flutter
- import Foundation
- class AudioEventHandler: NSObject, FlutterStreamHandler {
- private var eventSink: FlutterEventSink?
- private var isProcessing = false
-
- static func register(with registrar: FlutterPluginRegistrar) {
- let channel = FlutterEventChannel(
- name: "com.example.audio_events",
- binaryMessenger: registrar.messenger()
- )
- let instance = AudioEventHandler()
- channel.setStreamHandler(instance)
- }
-
- // 开始监听
- func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
- self.eventSink = events
- guard let audioData = arguments as? Data else {
- events(FlutterError(code: "INVALID_ARG", message: "参数不是音频数据", details: nil))
- return nil
- }
- processAudioStream(audioData)
- return nil
- }
-
- // 取消监听
- func onCancel(withArguments arguments: Any?) -> FlutterError? {
- eventSink = nil
- isProcessing = false
- return nil
- }
-
- // 模拟流式转写
- private func processAudioStream(_ data: Data) {
- isProcessing = true
- let queue = DispatchQueue.global()
-
- // 发送进度和片段
- queue.asyncAfter(deadline: .now() + 0.5) { [weak self] in
- self?.eventSink?(0.3)
- }
- queue.asyncAfter(deadline: .now() + 1.0) { [weak self] in
- self?.eventSink?("这是第一段转写文本")
- }
- queue.asyncAfter(deadline: .now() + 1.5) { [weak self] in
- self?.eventSink?(0.7)
- }
- queue.asyncAfter(deadline: .now() + 2.0) { [weak self] in
- self?.eventSink?("这是第二段转写文本")
- }
- queue.asyncAfter(deadline: .now() + 2.5) { [weak self] in
- self?.eventSink?(1.0)
- self?.eventSink?(FlutterEndOfEventStream)
- }
- }
- }
复制代码 3 消息通道适用场景分析
在实际开发中,选择哪种类型的Channel取决于具体的通信需求。理解每种Channel的特点和适用场景对于构建高效、可维护的Flutter应用至关重要。
通道类型核心能力适用场景典型案例BasicMessageChannel双向持续消息传递频繁数据交互、实时同步音频/视频流传输、即时通讯消息交换MethodChannel单次方法调用(请求-响应)调用原生API、获取单次结果设备信息获取、权限检查、单次数据处理EventChannel原生向Dart推送事件流连续数据推送、状态监听传感器数据(加速度、陀螺仪)、实时日志流场景选择建议:
- 如果需要双向频繁通信(如Flutter与原生持续交换音频帧):优先选择BasicMessageChannel,其设计更轻量,适合持续数据流动。
- 如果只需要单次调用并获取结果(如调用原生SDK完成一次音频转写):使用MethodChannel,代码更简洁,符合“方法调用”的直觉。
- 如果需要原生主动推送连续数据(如实时语音转写的逐句结果):EventChannel是最佳选择,通过Stream可自然处理流式数据。
4 音频处理框架设计
前文已分析了Flutter三种消息通道的特点及其适用场景,本章节我们就结合这些特点来设计一个音频处理框架,这个框架核心功能为:“将音频数据转成文本消息”,这里主要有4个功能点:
- Flutter层负责把音频的二进制数据发送给原生层;
- 原生层在收到音频数据后,调用第三方的服务完成音频转文本;
- 原生层并把音频转出来的文本结果发送回Flutter层展示;
- Flutter 层触发 “开始、结束、暂停、继续” 等控制指令,原生层响应并处理;
这4个功能点里,除了”原生层调用第三方服务转写音频“ 无需跨层交互外,其他的功能点都是均需要原生和Flutter层通过消息通道实现交互;
4.1 音频数据的传输
音频数据传输需重点考虑三方面:Flutter 层数据流的管理、跨平台通道的选择与数据传输、原生层的数据接收。
在Flutter层会通过StreamController来监听音频数据的变化(实现数据流管理),监听到音频数据的变化后,通过BasicMessageChannel通道(将音频数据)发送到原生层,并且由于传输的是二进制的数据,编码器采用了BinaryCodec来直接传输 —— 这一通信机制可避免不必要的数据复制与编码转换,最大化传输效率。- import 'dart:async';
- import 'dart:typed_data';
- import 'package:flutter/services.dart';
- class AudioTransferManager {
- // 定义BinaryCodec的BasicMessageChannel(名称需与原生层一致)
- static const BasicMessageChannel<ByteData> _audioChannel = BasicMessageChannel(
- 'com.example.audio_transfer/binary', // 通道唯一标识
- BinaryCodec(), // 直接传输二进制,避免额外编解码开销
- );
- // 管理音频数据流的控制器
- final StreamController<Uint8List> _audioStreamController = StreamController<Uint8List>.broadcast();
-
- // 音频数据流订阅对象(用于取消监听)
- StreamSubscription? _audioSubscription;
- // 初始化:启动音频采集并监听数据流
- void startAudioTransfer() {
- // 假设通过某个音频采集库获取原始音频流(如flutter_sound、audio_recorder等)
- Stream<Uint8List> audioStream = AudioRecorder.start(); // 伪代码:启动音频采集
-
- // 订阅音频流,实时发送数据到原生层
- _audioSubscription = audioStream.listen(
- (Uint8List audioData) {
- _sendAudioToNative(audioData);
- },
- onError: (error) {
- print('音频流错误: $error');
- },
- onDone: () {
- print('音频流结束');
- },
- );
- }
- // 发送音频二进制数据到原生层
- Future<void> _sendAudioToNative(Uint8List audioData) async {
- try {
- // 将Uint8List转换为ByteData(BinaryCodec要求的输入类型)
- final byteData = ByteData.view(audioData.buffer);
- // 发送数据(可根据需要等待原生层响应)
- await _audioChannel.send(byteData);
- } catch (e) {
- print('发送音频数据失败: $e');
- }
- }
- // 释放资源:停止传输并清理
- void stopAudioTransfer() {
- _audioSubscription?.cancel(); // 取消流订阅
- _audioStreamController.close(); // 关闭流控制器
- // 通知原生层停止处理(可选)
- _audioChannel.send(ByteData(0)); // 发送空数据作为停止信号
- }
- }
复制代码 原生层接收(以Android为例):
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.BasicMessageChannel
- import io.flutter.plugin.common.BinaryCodec
- import java.nio.ByteBuffer
- class AudioTransferHandler(flutterEngine: FlutterEngine) {
- init {
- // 注册与Flutter对应的BasicMessageChannel
- val channel = BasicMessageChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.example.audio_transfer/binary", // 与Flutter层通道名称一致
- BinaryCodec.INSTANCE
- )
- // 设置消息接收回调
- channel.setMessageHandler { message, reply ->
- // message为Flutter发送的ByteData,转换为字节数组
- val audioData = message?.array() // 二进制音频数据(uint8List对应byte[])
-
- if (audioData != null && audioData.isNotEmpty()) {
- // 处理音频数据(如写入文件、实时处理、转发等)
- processAudioData(audioData)
- } else {
- // 收到空数据,停止处理
- stopProcessing()
- }
-
- // 可选:向Flutter层发送响应(如确认接收)
- reply.reply(null)
- }
- }
- // 处理音频二进制数据
- private fun processAudioData(audioData: ByteArray) {
- // 示例:将音频数据写入缓冲区或调用第三方的服务,如azure,sonix,科大讯飞之类的
- // 注意:需在子线程处理,避免阻塞UI线程
- AudioProcessor.enqueue(audioData)
- }
- // 停止音频处理
- private fun stopProcessing() {
- AudioProcessor.clear()
- }
- }
复制代码 4.2 文本结果的传输
随着音频数据的持续输入,其转换后的文本信息需持续回传至 Flutter 层(原生→Flutter)。此处采用 EventChannel 将文本传输回 Flutter 层(也可使用 BasicMessageChannel,只需选择合适的解码器),核心通信逻辑为:通过 EventChannel 实现原生层向 Flutter 层的单向数据推送。
原生层进行文本的推送:
- import io.flutter.plugin.common.EventChannel
- import io.flutter.plugin.common.PluginRegistry.Registrar
- class AudioTransferHandler(flutterEngine: FlutterEngine) {
- private val eventChannel: EventChannel
- private var eventSink: EventChannel.EventSink? = null
- init {
- // 初始化EventChannel
- eventChannel = EventChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.audio.text/result_channel"
- )
- eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
- override fun onListen(arguments: Any?, sink: EventChannel.EventSink) {
- eventSink = sink
- // 初始化音频转文本处理器...
- }
- override fun onCancel(arguments: Any?) {
- eventSink = null
- // 释放资源...
- }
- })
- }
- // 推送文本结果到Flutter
- fun sendTextResult(text: String) {
- registrar.activity().runOnUiThread {
- eventSink?.success(text)
- }
- }
- // 其他音频处理逻辑...
- }
复制代码 Flutter层监听原生的推送:
- import 'dart:async';
- import 'package:flutter/services.dart';
- class AudioTransferManager {
- // 省略其他的代码
- // 文本结果回传的EventChannel
- static const EventChannel _textEventChannel = EventChannel(
- 'com.audio.text/result_channel',
- );
- // 文本结果流订阅
- StreamSubscription? _textSubscription;
- // 开始监听文本结果
- void startListening(void Function(String) onTextReceived) {
- _textSubscription = _textEventChannel.receiveBroadcastStream().listen(
- (data) {
- onTextReceived(data as String);
- },
- onError: (error) {
- // 错误处理...
- },
- );
- }
- // 停止监听并释放资源
- void stopListening() {
- _textSubscription?.cancel();
- }
- // 其他音频相关逻辑...
- }
复制代码 4.3 控制指令的传输
控制指令的交互类似接口方法调用,需返回调用结果,因此采用 MethodChannel 传输。该场景的通信机制为:通过 MethodChannel 传输 “开始、暂停” 等控制指令,同时接收原生层返回的指令执行结果(如 “启动成功”“已暂停”)。
原生层的监听对应方法的调用:
- import io.flutter.embedding.engine.FlutterEngine
- import io.flutter.plugin.common.MethodCall
- import io.flutter.plugin.common.MethodChannel
- import io.flutter.plugin.common.MethodChannel.MethodCallHandler
- class AudioControlHandler(flutterEngine: FlutterEngine) : MethodCallHandler {
- init {
- // 注册MethodChannel
- MethodChannel(
- flutterEngine.dartExecutor.binaryMessenger,
- "com.audio.control/command"
- ).setMethodCallHandler(this)
- }
- // 处理Flutter层的方法调用
- override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
- when (call.method) {
- "start" -> {
- // 解析参数
- val sampleRate = call.argument<Int>("sampleRate") ?: 16000
- // 执行启动逻辑...
- val success = true // 实际处理结果
- result.success(success) // 返回结果给Flutter
- }
- "stop" -> {
- // 执行停止逻辑...
- result.success("stopped_successfully") // 返回字符串结果
- }
- // 这里可增加更多的控制方法,如pause,resume
- else -> {
- result.notImplemented() // 未实现的方法
- }
- }
- }
- // ... 其他原生处理逻辑
- }
复制代码 Flutter层的注册对应方法的调用:
- import 'package:flutter/services.dart';
- class AudioControlChannel {
- // 定义MethodChannel(通道名称需与原生层一致)
- static const MethodChannel _methodChannel = MethodChannel(
- 'com.audio.control/command',
- );
- // 发送控制指令并获取返回结果(示例:启动音频处理)
- Future<bool> startProcessing() async {
- try {
- // 调用原生方法,传入参数(可选)
- final result = await _methodChannel.invokeMethod<bool>(
- 'start', // 方法名
- {'sampleRate': 16000, 'channel': 1}, // 可选参数
- );
- return result ?? false;
- } on PlatformException catch (e) {
- print('控制指令调用失败: ${e.message}');
- return false;
- }
- }
- // 其他控制方法(示例:停止音频处理)
- Future<String> stopProcessing() async {
- final result = await _methodChannel.invokeMethod<String>('stop');
- return result ?? 'stopped';
- }
- // ... 其他控制指令方法(如暂停、配置参数等)
- }
复制代码 5 总结
本文 聚焦 Flutter 开发中 Dart 层与原生层的通信需求,先介绍了 BasicMessageChannel、MethodChannel、EventChannel 三种核心消息通道,接着详细解析各通道的核心能力、具体代码示例及独特特点,随后分析它们的适用场景并给出选择建议,最后结合音频处理框架的设计,举例说明各消息通道在实际开发中的应用(
- BasicMessageChannel:音频二进制数据传输(Flutter→原生)
- EventChannel:文本结果流推送(原生→Flutter)
- MethodChannel:控制指令调用与结果返回(双向,带返回值))
助力开发者理解其使用方式。
6 参考
- platform-channels
- EvenChannel
- BasicMessageChannel
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |