惊爆!Flutter消息通道的超神全解析!

本文首发于公众号:移动开发那些事:惊爆!Flutter消息通道的超神全解析!

Flutter 跨平台开发中,Dart 层 与原生层(Android/iOS)的通信是核心需求之一。无论是调用摄像头、麦克风等设备硬件能力,获取系统信息,还是处理高性能计算任务,均需通过消息通道实现数据交互。Flutter提供了三种核心消息通道,每种通道都有其独特的设计理念和适用场景。本文将详细解析这三种通道,并结合音频处理框架的设计,帮助开发者深化对各通道使用场景的理解。

2 消息通道类型详解

Flutter 的消息通道本质上是 Dart 层与原生层(Platform Channel)之间的通信桥梁,基于二进制流传输数据,通过编解码器(Codec)实现不同类型数据的序列化与反序列化。目前,Flutter主要提供了三种通道类型:BasicMessageChannelMethodChannelEventChannel

2.1 BasicMessageChannel

BasicMessageChannel 用于Dart层与原生层之间的双向持续消息的传输 ,支持字符串、二进制、自定义对象等任意类型数据交互,适用于需频繁数据交换的场景。它依赖MessageCodec进行数据编码 / 解码,默认使用支持基础类型、列表、映射的 StandardMessageCodec,也可自定义 StringCodecBinaryCodec 等编解码器

下面以一个简单的从Flutter层发送音频数据到原生层,原生层再把音频数据转成文本再发送回Flutter的例子来解释其的使用:

Flutter 端(Dart)

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)

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<Any> = 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)

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的示例,在实际的应用过程中, BasicMessageChannelnamecodec 一定要三个端都保持一致(Dart,Android,iOS)

此外,需注意一个实践细节:许多文档提及传输二进制数据时,使用 BinaryCodec 解码器效率最高,但实际测试发现,在 Android 平台中,BasicMessageChannel + BinaryCodec 存在特定 Bug------ 原生层发送至 Flutter 层的数据(无论原生层如何处理 ByteBuffer)始终为空(Flutter 层发送的数据可被原生层正常接收解析);相关讨论可参考 github.com/flutter/flu...

2.2 MethodChannel

MethodChannel 用于 Dart 层调用原生层方法(或原生层调用 Dart 层方法),支持同步与异步调用,适用于单次请求 - 响应式交互场景。它采用 "方法名 + 参数" 的通信模式,Dart 层通过 invokeMethod 调用原生方法,原生层则通过 MethodCallHandler 处理请求并返回结果。

MethodChannel支持的数据类型包括基本类型(boolean, int, double等)、字符串、列表和映射等,这些类型在消息中的序列化和反序列化会自动进行。同时,MethodChannel 具备全双工通信能力:Flutter 可主动向原生端发送消息并接收响应,原生端也可主动向 Flutter 端发送消息,待 Flutter 处理后接收返回结果。

简单的代码示例

Flutter 端(Dart)

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)

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)

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)

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)

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)

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来直接传输 ------ 这一通信机制可避免不必要的数据复制与编码转换,最大化传输效率。

dart 复制代码
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为例):

kotlin 复制代码
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 层的单向数据推送。

原生层进行文本的推送:

kotlin 复制代码
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层监听原生的推送:

javascript 复制代码
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 传输 "开始、暂停" 等控制指令,同时接收原生层返回的指令执行结果(如 "启动成功""已暂停")。

原生层的监听对应方法的调用:

kotlin 复制代码
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层的注册对应方法的调用:

dart 复制代码
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 层与原生层的通信需求,先介绍了 BasicMessageChannelMethodChannel、EventChannel 三种核心消息通道,接着详细解析各通道的核心能力、具体代码示例及独特特点,随后分析它们的适用场景并给出选择建议,最后结合音频处理框架的设计,举例说明各消息通道在实际开发中的应用(

  • BasicMessageChannel:音频二进制数据传输(Flutter→原生)
  • EventChannel:文本结果流推送(原生→Flutter)
  • MethodChannel:控制指令调用与结果返回(双向,带返回值)) 助力开发者理解其使用方式。

6 参考

相关推荐
_阿南_20 小时前
Riverpod3.0.0替换StateProvider和ChangeNotifierProvider
flutter
用户0920 小时前
Flutter构建速度深度优化指南
android·flutter·ios
w_y_fan21 小时前
Flutter中的沉浸式模式设置
前端·flutter
程序员老刘1 天前
跨平台开发地图:客户端技术选型指南 | 2025年9月
flutter·客户端
傅里叶1 天前
Flutter用户体验之01-避免在 build() 或 initState() 内直接做耗时 blocking
前端·flutter
阿笑带你学前端1 天前
Flutter本地通知系统:记账提醒的深度实现
前端·flutter
孤鸿玉2 天前
Fluter InteractiveViewer 与ScrollView滑动冲突问题解决
flutter
叽哥2 天前
Flutter Riverpod上手指南
android·flutter·ios
BG3 天前
Flutter 简仿Excel表格组件介绍
flutter