You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:02:13