window: extract XWindow primitive; XNotifications stops carrying focus
The window-focus signal had no business living on the notifications primitive — it was there because the same NotificationsPlugin happened to observe NSApplication active/resign for its own reasons. Splitting it into a sibling XWindow primitive (with its own WindowPlugin on macOS, ux/window/events) lets future consumers — paused video, deferred-work scheduling, dock badge counts — read focus state without pulling in UNUserNotificationCenter. XNotifications now only exposes notification I/O (show/cancel + tap + authorization). The 'type:focus' event-channel branch is gone.
This commit is contained in:
@@ -2,10 +2,10 @@ import AppKit
|
||||
import FlutterMacOS
|
||||
import UserNotifications
|
||||
|
||||
/// `ux/notifications` + `ux/notifications/events`. Generic OS-notification
|
||||
/// + app-focus surface used by hosts that want native Notification Center
|
||||
/// entries on macOS. Domain-agnostic — the Dart caller decides when to
|
||||
/// emit and how to format the payload.
|
||||
/// `ux/notifications` + `ux/notifications/events`. Domain-agnostic native
|
||||
/// OS-notification surface: show / cancel via the method channel, tap +
|
||||
/// authorization changes via the event channel. Window-focus state lives
|
||||
/// in `WindowPlugin` (see `XWindow` on the Dart side).
|
||||
public class NotificationsPlugin: NSObject, NativePlugin, FlutterStreamHandler,
|
||||
UNUserNotificationCenterDelegate
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public class NotificationsPlugin: NSObject, NativePlugin, FlutterStreamHandler,
|
||||
eventChannel = events
|
||||
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
observeAppActivation()
|
||||
}
|
||||
|
||||
// MARK: - FlutterStreamHandler
|
||||
@@ -41,9 +40,6 @@ public class NotificationsPlugin: NSObject, NativePlugin, FlutterStreamHandler,
|
||||
eventSink events: @escaping FlutterEventSink
|
||||
) -> FlutterError? {
|
||||
eventSink = events
|
||||
// Seed the initial focus state so Dart-side starts in a deterministic
|
||||
// value rather than the constructor default.
|
||||
emit(["type": "focus", "focused": NSApp.isActive])
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -171,32 +167,6 @@ public class NotificationsPlugin: NSObject, NativePlugin, FlutterStreamHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - App-activation observers
|
||||
|
||||
private func observeAppActivation() {
|
||||
let nc = NotificationCenter.default
|
||||
nc.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidBecomeActive),
|
||||
name: NSApplication.didBecomeActiveNotification,
|
||||
object: nil,
|
||||
)
|
||||
nc.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidResignActive),
|
||||
name: NSApplication.didResignActiveNotification,
|
||||
object: nil,
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func onDidBecomeActive() {
|
||||
emit(["type": "focus", "focused": true])
|
||||
}
|
||||
|
||||
@objc private func onDidResignActive() {
|
||||
emit(["type": "focus", "focused": false])
|
||||
}
|
||||
|
||||
// MARK: - UNUserNotificationCenterDelegate
|
||||
|
||||
public func userNotificationCenter(
|
||||
|
||||
68
macos/Classes/WindowPlugin.swift
Normal file
68
macos/Classes/WindowPlugin.swift
Normal file
@@ -0,0 +1,68 @@
|
||||
import AppKit
|
||||
import FlutterMacOS
|
||||
|
||||
/// `ux/window/events`. Reports host-application focus state — emits
|
||||
/// `{type: "focus", focused: Bool}` whenever the active NSApplication
|
||||
/// flips. Seeded from `NSApp.isActive` when Dart subscribes so the
|
||||
/// emitter starts in a deterministic value.
|
||||
public class WindowPlugin: NSObject, NativePlugin, FlutterStreamHandler {
|
||||
private var eventChannel: FlutterEventChannel?
|
||||
private var eventSink: FlutterEventSink?
|
||||
|
||||
public func register(with registrar: FlutterPluginRegistrar) {
|
||||
let events = FlutterEventChannel(
|
||||
name: "ux/window/events",
|
||||
binaryMessenger: registrar.messenger,
|
||||
)
|
||||
events.setStreamHandler(self)
|
||||
eventChannel = events
|
||||
|
||||
observeAppActivation()
|
||||
}
|
||||
|
||||
// MARK: - FlutterStreamHandler
|
||||
|
||||
public func onListen(
|
||||
withArguments arguments: Any?,
|
||||
eventSink events: @escaping FlutterEventSink
|
||||
) -> FlutterError? {
|
||||
eventSink = events
|
||||
emit(["type": "focus", "focused": NSApp.isActive])
|
||||
return nil
|
||||
}
|
||||
|
||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||
eventSink = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Observers
|
||||
|
||||
private func observeAppActivation() {
|
||||
let nc = NotificationCenter.default
|
||||
nc.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidBecomeActive),
|
||||
name: NSApplication.didBecomeActiveNotification,
|
||||
object: nil,
|
||||
)
|
||||
nc.addObserver(
|
||||
self,
|
||||
selector: #selector(onDidResignActive),
|
||||
name: NSApplication.didResignActiveNotification,
|
||||
object: nil,
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func onDidBecomeActive() {
|
||||
emit(["type": "focus", "focused": true])
|
||||
}
|
||||
|
||||
@objc private func onDidResignActive() {
|
||||
emit(["type": "focus", "focused": false])
|
||||
}
|
||||
|
||||
private func emit(_ event: [String: Any]) {
|
||||
eventSink?(event)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public class XPlugin: NSObject, FlutterPlugin {
|
||||
UxVideoPlayerPlugin(),
|
||||
UrlPlugin(),
|
||||
NotificationsPlugin(),
|
||||
WindowPlugin(),
|
||||
]
|
||||
for plugin in plugins {
|
||||
plugin.register(with: registrar)
|
||||
|
||||
Reference in New Issue
Block a user