本來以為 3.44 至少會釋出 Windows 的多視窗,結果只宣布了個 **Canonical 成為 Flutter Desktop 的主要維護者和策略合作夥伴** ,實際上很久前 Flutter 就把桌面端交給對方維護,這兩年也基本是這麼在推進,只是速度確實太慢:

這次更新裡主要提到了 showRawDialog / showDialog 走原生 dialog window ,如果你 flutter config --enable-windowing 開啟了 windowing,那麼 dialog 會透過 windowing system 顯示在自己的視窗,而不是目前視窗內的 modal overlay,平台不支援時,會 fallback 到一般 dialog route。
這個問題倒是把我坑了,因為我一直用的是 beta 版本,這次用了多視窗之後,很多 loading 和彈窗直接跑到新視窗上,然後又沒適配,直接 UX 都亂了。
另外目前在介面層面,大部分 win 介面已經可用了:
RegularWindowController / RegularWindowDialogWindowController / DialogWindowTooltipWindowController / TooltipWindowPopupWindowController / PopupWindowSatelliteWindowController / SatelliteWindow不過多視窗在 Flutter 裡確實挺多邊界問題,因為框架一開始就沒設計多視窗概念,所以很多東西都是在重構,現在的多視窗實作是一個 Flutter engine 實例能夠渲染多個獨立的作業系統視窗,而這些視窗共享同一個 Dart isolate 和 widget tree,然後透過 view ID 區分不同的渲染目標,所以多視窗之間的競態細節會比較多。
不過問題更多還是在於平台適配,從零開始做一套脫離平台渲染的多視窗,還真的難度不小。
根據我體驗下來,Win 11 上基本都還行,用起來問題不大,就是多視窗焦點切換還有點互動支援不夠友善,但是在 Win 10 上目前問題應該比較多:
比如在一些 Win10 上,開關視窗、啟用、最大化 這些基礎功能在多視窗都還有問題,主要是 WM_SIZE 與等待機制在 Win10 上的行為不一致。
目前 Flutter Win embedder 處理視窗大小變化(包括最大化、多視窗建立/關閉時的尺寸變化)的核心邏輯是:
WM_SIZE 訊息(resize、maximize 等觸發)flutter_windows_view.cc 中執行一個 condition_variable + mutex ,逾時是 100ms,持續等待直到 Flutter engine 產出一幀與新目標尺寸匹配的畫面這個等待機制是 Flutter Win 主要為了防止出現畫面撕裂和黑屏閃爍的,而問題就在這裡,Win10 在某些視窗操作(尤其是最大化,以及多視窗場景下的建立/啟用/關閉)時,WM_SIZE 訊息時序導致等待提前逾時或條件變數通知時序錯亂,跟 Win11 又居然不一樣,這就導致等待的觸發路徑跟不上了。
另外 Win 10 和 Win11 在視窗合成也有差異:
DWM(Desktop Window Manager)行為差異:
WS_EX_LAYERED 與 WM_NCCALCSIZE 處理差異:
WM_NCCALCSIZE 的處理有行為差異,會影響 resize 時的中間狀態是否可見當然,更大的問題其實是多視窗場景放大了這些問題:
另外還可能存在點擊問題,居然會有在第一幀 layout 完成之前就被分發進來的點擊事件,這些問題都是相當細節的具體場景,目前我整體體驗 Win11 還行,就是焦點切換還不太友善。
另外 macOS 上也是類似,主要問題還是視窗在第一幀渲染完成之前就顯示了,而且問題在 Intel Mac(macOS 15.7.5)上可以穩定復現,在 M2 MacBook Pro(macOS 26.4)上又不穩定復現,不得不說,現在 macOS 的版本號碎片化也很豐富了。
那為什麼這個問題在 macOS 上也會發生?因為 macOS 的視窗顯示機制:
NSWindow 和 NSView,視窗建立後如果呼叫 makeKeyAndOrderFront:,視窗就會立即可見而 RegularWindowController 目前又缺少:
隱藏/延遲建立能力:建立視窗但不立即顯示,等待就緒訊號
每視窗的「首幀已呈現」訊號:沒有一個回呼或事件能告知上層「這個視窗的第一幀已渲染完畢」
建立時的初始背景色:視窗在內容就緒前沒有正確的背景顏色,導致顯示黑色或透明狀態
所以這就導致了 macOS 上視窗時序沒辦法嚴格遵循,而實際上這個問題在單視窗場景下就一直有,很久之前 #55427(2020 年提的) 就有類似:
Consider hiding windows until the engine is active in the desktop runner templates 。
這上面的 macOS 問題一直都還沒被修復,因為系統機制造成,Windows 的修復思路很直接,在 runner 模板裡建立視窗時加 WS_VISIBLE 為 false,然後等 engine 的 first-frame callback 觸發後再 ShowWindow,因為 Win32 API 設計上支援這種模式,callback 機制也在 engine 裡已經存在。
而 macOS 就比較麻煩了,AppKit 的 orderFront: 和 makeKeyAndOrderFront: 是立即生效的,沒有 deferred 參數。
也就是根據需求,需要在 embedder 層向 engine 註冊 per-view 的 first-frame 回呼,而這個機制在 macOS embedder 沒有可用實作,目前 macOS embedder 的 first-frame 通知是 engine 級別的,不是 view 級別的,多視窗下無法精確知道「哪個 view 的第一幀已就緒」。
當然,在 Tooltip/Popup 場景下會好一點,因為用了 alphaValue = 0.0 + positioner 回呼來延遲顯示,但是也是治標不治本,這也是 macOS 目前最大痛點。
而 Linux 就更糟心了,相信用 Linux 的應該都懂,這就不是人力問題,而是 GTK3 的 OpenGL 上下文架構從設計上就不支援多個 GtkGLArea 共享同一個 GL 上下文,這導致多 view 渲染在 Linux 上面臨需要繞過 GTK 底層限制的工程問題,感受一下:
時間事件2023 年 11 月issue #138178 由 dkwingsmt 建立,標註 "mostly for tracking purposes",無人認領2024 年 7 月engine PR #54018 作為第一步合入(僅是基礎結構調整)2024 年 10 月prototype 可用,正在拆分為可提交的 PR;同時揭示了核心技術問題(GTK3 GL 上下文限制)2024 年 10 月engine PR #55541(view ID 分配)、#55542(view ID 釋放)合入,這是僅有的兩個實質進展2025 年 6 月robert-ancell 提交 draft PR #170045,嘗試用 EGL 繞過 GTK3 限制,但標註 "not working on X11"2025 年 7 月PR #170045 被關閉,未合入,X11 fallback 未完成2026 年 1 月bot 自動提醒 assignee 沒有進展,robert-ancell 回覆 "Still chipping away at this"(仍在做)2026 年 3 月issue #183911 建立,指出 Linux embedder 多 view 下 shader 需要共享,是又一個新的 P2 子問題2026 年 5 月bot 再次提醒無進展,assignee 被系統自動移除,issue 目前無人認領GTK3 的 GtkGLArea 每個實例都有獨立的 GdkGLContext,這些上下文之間預設不能共享 texture、framebuffer 等 GL 資源。
在單視窗場景下,Flutter engine 把畫面渲染到一個 GdkGLContext 裡就結束了,但是啊,多視窗下 engine 需要把不同 view 的畫面分別 present 到對應的 GtkGLArea,而各自的 GL 上下文是隔離的,engine 沒辦法直接把一個 view 的渲染結果跨上下文傳遞給另一個視窗。
而 prototype 採用的繞過方案,在 implicit view(第一個視窗)的 GL 上下文裡渲染所有內容,然後透過 CPU 回讀再寫入其他視窗的上下文,但是這一聽就知道多不靠譜,CPU readback 本身就是 GPU pipeline 的效能殺手。
另外還有提到用獨立的 EGL 上下文取代 GdkGLContext,EGLImage 是一種可以在 EGL 上下文之間共享 texture 的機制,不需要 CPU 拷貝。
但是,但是,但是這個方案在 X11 上不工作啊 ,X11 和 Wayland 在 EGL 的實作細節上有差異,EGLImage 在 X11 上的驅動支援沒有在 Wayland 上那麼普遍,而且 X11 的 GdkGLContext 是基於 GLX 而不是 EGL 的,和 EGL-based 的 Flutter context 之間需要額外的 interop,這部分還沒有實作,所以路子又窄了。
這也是 Linux 的另一個獨特麻煩,需要同時支援 X11 和 Wayland 兩套顯示協定,而這兩者在底層 GL/EGL 棧上的行為有明顯差異:
EGLImage 擴充支援普遍,EGL context 和 GTK4/Wayland surface 的整合有官方文件EGL_EXT_platform_x11),擴充的驅動支援也不如 Wayland 普遍,開源驅動(Mesa)和私有驅動(NVIDIA)行為不一致這也是為什麼很多對支援 Linux 不熱衷的原因。
不管怎麼說,多視窗確實推進得有些久了,Canonical 的投入也一直不溫不火,感覺 AI 時代了,再不加速推進 release,感覺多視窗就要爛在手裡了,不過目前至少我在 Win11 場景上還行。