如何从Flutter向iOS Swift发送Stream<List>并在Swift中处理
问题描述
我正在开发一款iOS应用,希望使用自己用Flutter编写的SDK,该SDK中有一个scan方法会返回Stream<List<FoundDevice>>。我需要从Swift原生代码调用这个scan方法,并在Swift中接收和处理该Stream<List<FoundDevice>>。
尝试用Flutter的MethodChannel处理Swift的调用并启动扫描,方法处理器返回Stream<List<FoundDevice>>,但不知道如何将该流发送到Swift并处理。
Flutter 现有代码
const methodChannel = MethodChannel('com.example/my_method_channel'); Future<Stream<List<FoundDevice>>> methodHandler(MethodCall call) { final String idea = call.arguments; switch (call.method) { case "scan": return _handleScanCall(); default: print('no method handler for method ${call.method}'); throw MissingPluginException(); } } Future<Stream<List<FoundDevice>>> _handleScanCall() async { print('scan called from iOS native code'); return TrackbarSDK.instance.scan(); } Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); methodChannel.setMethodCallHandler(methodHandler); }
Swift 现有代码
class ViewController: UIViewController { var methodChannel: FlutterMethodChannel? override func viewDidLoad() { super.viewDidLoad() let appDelegate = UIApplication.shared.delegate as! AppDelegate methodChannel = FlutterMethodChannel(name: "com.example/my_method_channel", binaryMessenger: appDelegate.flutterEngine.binaryMessenger) startScan() } func startScan() { methodChannel?.invokeMethod("scan", arguments: nil, result: { (result: Any?) in // 这里需要接收并处理Stream<List<FoundDevice>> }) } }
解决方案
MethodChannel仅适用于单次请求-响应场景,无法直接返回流式数据。要处理持续的数据流,应该使用Flutter的EventChannel,它专门用于双向流式通信。具体实现步骤如下:
1. Flutter端改造:添加EventChannel并发送流数据
新增EventChannel用于向原生端推送扫描数据,保留MethodChannel用于触发扫描/停止扫描的动作:
const methodChannel = MethodChannel('com.example/my_method_channel'); const eventChannel = EventChannel('com.example/my_event_channel'); StreamSubscription<List<FoundDevice>>? _scanSubscription; Future<void> methodHandler(MethodCall call) async { switch (call.method) { case "scan": // 触发扫描(实际数据流通过EventChannel推送) break; case "stopScan": _handleStopScan(); break; default: print('no method handler for method ${call.method}'); throw MissingPluginException(); } } void _handleStopScan() { _scanSubscription?.cancel(); _scanSubscription = null; } Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); methodChannel.setMethodCallHandler(methodHandler); // 配置EventChannel的流处理器 eventChannel.setStreamHandler(StreamHandler( onListen: (arguments, events) async { // 原生端订阅时启动扫描,并推送数据 await _scanSubscription?.cancel(); final scanStream = TrackbarSDK.instance.scan(); _scanSubscription = scanStream.listen((devices) { // 将FoundDevice转为原生可解析的Map数组 final deviceMaps = devices.map((device) => { "id": device.id, "name": device.name, "rssi": device.rssi, // 补充其他需要传递的字段 }).toList(); // 推送扫描结果到原生 events.success(deviceMaps); }, onError: (error) { // 推送错误信息 events.error("SCAN_ERROR", error.toString(), null); }, onDone: () { // 通知流结束 events.endOfStream(); }); }, onCancel: (arguments) async { // 原生端取消订阅时停止扫描 _handleStopScan(); }, )); }
2. Swift端改造:监听EventChannel并处理数据流
先定义Swift端的FoundDevice模型,再配置EventChannel监听数据流:
定义Swift模型
struct FoundDevice { let id: String let name: String let rssi: Int // 对应Flutter端的其他字段 }
修改ViewController代码
class ViewController: UIViewController { var methodChannel: FlutterMethodChannel? var eventChannel: FlutterEventChannel? override func viewDidLoad() { super.viewDidLoad() let appDelegate = UIApplication.shared.delegate as! AppDelegate let binaryMessenger = appDelegate.flutterEngine.binaryMessenger // 初始化MethodChannel(用于触发停止扫描) methodChannel = FlutterMethodChannel(name: "com.example/my_method_channel", binaryMessenger: binaryMessenger) // 初始化EventChannel(监听扫描数据流) eventChannel = FlutterEventChannel(name: "com.example/my_event_channel", binaryMessenger: binaryMessenger) // 开始监听扫描流 eventChannel?.setStreamHandler(self) } func stopScan() { methodChannel?.invokeMethod("stopScan", arguments: nil) } deinit { stopScan() } } // 实现FlutterStreamHandler协议,处理数据流 extension ViewController: FlutterStreamHandler { func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { // 处理Flutter推送的事件 events = { event in switch event { case let deviceMaps as [[String: Any]]: // 解析为Swift的FoundDevice数组 let devices = deviceMaps.compactMap { map in guard let id = map["id"] as? String, let name = map["name"] as? String, let rssi = map["rssi"] as? Int else { return nil } return FoundDevice(id: id, name: name, rssi: rssi) } // 处理扫描到的设备列表 self.handleScannedDevices(devices) case let error as FlutterError: print("扫描错误: \(error.message ?? "")") case nil: print("扫描结束") default: print("未知事件类型") } } return nil } func onCancel(withArguments arguments: Any?) -> FlutterError? { stopScan() return nil } // 自定义业务处理方法 func handleScannedDevices(_ devices: [FoundDevice]) { print("扫描到\(devices.count)个设备") for device in devices { print("设备ID: \(device.id), 名称: \(device.name), 信号强度: \(device.rssi)") // 这里可以添加更新UI、存储设备等业务逻辑 } } }
关键说明
- EventChannel适配流式场景:EventChannel专为持续数据流设计,完美匹配Flutter的
Stream类型,而MethodChannel仅适合单次请求响应。 - 数据序列化规则:Flutter与原生之间只能传递基础数据类型(字符串、数字、布尔值)及其集合,因此必须将
FoundDevice转为Map后推送,再在Swift端解析为对应模型。 - 资源清理:在Swift的
deinit方法中调用停止扫描,Flutter端取消StreamSubscription,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Ahmed Osama
相关产品推荐
相关产品推荐

