如何通过EventChannel将macOS原生NSColorPanel的颜色选择流传输至Flutter?解决macos_ui插件中颜色选择事件无法回调的问题
解决macOS原生颜色选择器NSColorPanel的事件流式传输到Flutter的问题
我仔细看了你的代码,发现核心问题是事件传递的链路没有打通——你在ColorPanelProvider里实现了FlutterStreamHandler但并没有把它注册到Flutter的EventChannel上,它的eventSink根本没法把事件传给Flutter端。另外还有几处细节问题导致颜色变化事件无法正确触发,下面一步步给你修正:
问题拆解
- 重复的StreamHandler实现:
MacOSUiPlugin已经被注册为EventChannel的StreamHandler,但ColorPanelProvider也实现了这个协议,却没有和Flutter通道关联,导致它的eventSink调用完全无效。 - 通知注册重复:每次调用
startStream都会添加新的通知观察者,会导致颜色变化时多次触发回调。 - NSColorPanel配置顺序错误:
isContinuous应该在打开面板前设置,否则连续颜色变化的事件不会触发。
修正后的代码实现
第一步:重构ColorPanelProvider
去掉多余的FlutterStreamHandler实现,改用闭包回调的方式把颜色变化传递给MacOSUiPlugin:
import Cocoa class ColorPanelProvider: NSObject { let colorPanel = NSColorPanel.shared var colorChangeCallback: ((String) -> Void)? func openPanel() { // 先配置面板属性,再打开 colorPanel.isContinuous = true colorPanel.setTarget(self) colorPanel.setAction(#selector(colorDidChange)) colorPanel.makeKeyAndOrderFront(self) // 发送初始颜色 sendCurrentColor() } @objc private func colorDidChange() { sendCurrentColor() } private func sendCurrentColor() { let hexColor = colorPanel.color.asFlutterHexString print("currentColor: \(hexColor)") colorChangeCallback?(hexColor) } // 清理资源 func cleanUp() { colorPanel.setTarget(nil) colorPanel.setAction(nil) colorChangeCallback = nil } } extension NSColor { var asFlutterHexString: String { let red = Int(round(self.redComponent * 0xFF)) let green = Int(round(self.greenComponent * 0xFF)) let blue = Int(round(self.blueComponent * 0xFF)) return String(format: "0xFF%02X%02X%02X", red, green, blue) } }
第二步:修正MacOSUiPlugin
让它负责管理EventChannel的流,并把ColorPanelProvider的颜色变化回调和eventSink关联:
import Cocoa import FlutterMacOS public class MacOSUiPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { private let colorPanelProvider: ColorPanelProvider private var eventSink: FlutterEventSink? init(colorPanelProvider: ColorPanelProvider) { self.colorPanelProvider = colorPanelProvider super.init() // 设置颜色变化回调,关联到eventSink self.colorPanelProvider.colorChangeCallback = { [weak self] hexColor in self?.eventSink?(hexColor) } } public static func register(with registrar: FlutterPluginRegistrar) { let methodChannel = FlutterMethodChannel( name: "dev.groovinchip.macos_ui", binaryMessenger: registrar.messenger) let eventChannel = FlutterEventChannel( name: "dev.groovinchip.macos_ui/color_panel", binaryMessenger: registrar.messenger) let colorPanelProvider = ColorPanelProvider() let instance = MacOSUiPlugin(colorPanelProvider: colorPanelProvider) eventChannel.setStreamHandler(instance) registrar.addMethodCallDelegate(instance, channel: methodChannel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "color_panel": colorPanelProvider.openPanel() result(true) default: result(FlutterMethodNotImplemented) } } public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { print("listening to color panel events") eventSink = events // 如果面板已经打开,发送当前颜色 if colorPanelProvider.colorPanel.isVisible { colorPanelProvider.sendCurrentColor() } return nil } public func onCancel(withArguments arguments: Any?) -> FlutterError? { eventSink = nil colorPanelProvider.cleanUp() return nil } }
关键修正点说明
- 闭包替代双StreamHandler:用
colorChangeCallback把ColorPanelProvider的颜色变化事件传递给MacOSUiPlugin,再通过后者的eventSink发送到Flutter,保证链路唯一且有效。 - 正确配置NSColorPanel:把
isContinuous = true放在打开面板前,这样拖动颜色滑块时会连续触发colorDidChange事件,符合流式传输的需求。 - 清理资源:在
onCancel里调用cleanUp,避免内存泄漏,同时取消面板的target和action。 - 初始颜色发送:打开面板时和监听流时都发送当前颜色,保证Flutter端能拿到初始值。
Flutter端接收示例
你可以在Flutter端这样监听EventChannel:
final _colorPanelEventChannel = EventChannel('dev.groovinchip.macos_ui/color_panel'); StreamSubscription? _colorSubscription; void startListening() { _colorSubscription = _colorPanelEventChannel.receiveBroadcastStream().listen((hexColor) { // 处理收到的颜色,比如转换成Color对象 final color = Color(int.parse(hexColor)); print('Received color: $color'); }); } // 记得在适当的时候取消订阅 void stopListening() { _colorSubscription?.cancel(); }
这样修改后,NSColorPanel的颜色变化就能实时流式传递到Flutter端了。
内容的提问来源于stack exchange,提问作者GroovinChip
相关产品推荐
相关产品推荐

