7.1 KiB
7.1 KiB
Flutter Upgrade Checklist
Read this before changing FLUTTER_VERSION in .github/workflows/build.yaml or .fvmrc. Each entry is a
workaround tied to a Flutter bug: what it does, where it lives, and what has to be true before it can go. Remove an
entry together with its workaround; add one whenever a new workaround is pinned to the SDK.
Stable cadence
Flutter ships a stable release about once a quarter, three minor versions apart (3.41 in February, 3.44 in May, 3.47
in August 2026). The two minors in between (3.48, 3.49) are beta only, so a fix that lands on main reaches stable
in the next multiple, 3.50, expected around mid-November 2026. Hotfix releases (3.47.x) carry only cherry-picks
listed in the SDK's CHANGELOG.md.
Impeller disabled on Intel macOS release builds
- Bug: flutter/flutter#191538. From 3.47, when Impeller became the default desktop renderer, Intel (x86_64) Macs flicker across the whole window while scrolling or animating, and text can render garbled. Apple Silicon is not affected.
- Fix upstream: landed on
mainbefore 2026-08-25 and verified on3.48.0-1.0.pre-404; it is not in any 3.47.x hotfix (checked through 3.47.6). - Workaround: the release job for
macos-15-intel(arch: amd64) runsplutil -replace FLTEnableImpeller -bool NO macos/Runner/Info.plistbeforedart setup.dart, so only the Intel DMG falls back to Skia. The checked-inInfo.plistdoes not set the key, so local builds and the arm64 DMG keep Impeller. The macOS embedder reads the key at launch inFlutterDartProject.mm(enableImpeller), release builds included. - Side effect: Skia on Intel renders blur, shadows and text slightly differently; judge Intel-only visual reports against Skia, not Impeller.
- Remove when:
FLUTTER_VERSIONmoves to a stable that contains the fix (3.50 or later, or a 3.47.x whose changelog lists #191538). Before dropping the step, build the Intel DMG without it and scroll the proxies and dashboard pages on an Intel Mac with its integrated GPU driving the built-in display; an external display can switch to the discrete GPU and hide the flicker.
Wrong-size back buffer on macOS
- Bug: flutter/flutter#185394.
removeSurfaceForSize:in the macOS engine'sFlutterSurfaceManager.mmhands out the youngest idle cached surface without checking its size. When the window flips between sizes while the window server still holds the previous surface, which dragging a window edge back and forth does, a request for one size gets a surface of another. Impeller's Metal texture then disagrees with its descriptor, the render target drops it, andCanvas::SetupRenderPassdereferences a null texture on the raster thread. Debug builds logThe texture and its descriptor disagree about its sizeandStore action needs resolve but no valid resolve texture specifiedfirst; release builds just crash. - Fix upstream: flutter/flutter#192522, still open; 3.47.6
ships the same
FlutterSurfaceManager.mmas 3.47.4. - Workaround:
BackBufferCacheGuardin thewindowplugin'smacos/window/Sources/window/BackBufferCacheGuard.swift, installed fromWindowPlugin.register, replaces-[FlutterBackBufferCache removeSurfaceForSize:]at runtime and answers nil when the cached surface is not the requested size, so the engine allocates a new one. It does nothing if the engine no longer has that class or selector. - Remove when:
FLUTTER_VERSIONmoves to a stable that contains #192522 (check the SDKCHANGELOG.mdfor a 3.47.x cherry-pick). Before dropping it, run a debug build without the guard on that SDK and drag a window edge back and forth for a minute; neither validation line may appear.
Windows view visibility forwarded by the runner
- Bug: the Windows embedder's
WindowsLifecycleManagercounts the Flutter view as a window of its own and keeps it visible until the view is sized to zero. Hiding the top-level window never resizes the view, and thewindowplugin keeps it sized while minimized, so the app was never toldhidden: animations kept rendering in the tray andappVisibleProviderstayed true. - Workaround:
WindowPluginForwardVisibilityin thewindowplugin'swindows/include/window/window_plugin_runner.hhands the top-level window'sWM_SHOWWINDOWandWM_SIZEtoProcessExternalWindowMessagefor the view's HWND, so the view shows and hides with its window.FlutterWindow::MessageHandlerinwindows/runner/flutter_window.cppcalls it beforeHandleTopLevelWindowProc; the Flutter runner template has no such call, so keep it when regenerating the runner. - Remove when: the engine hides the view together with its top-level window. Check with the proxy running and
the window focused: hiding it to the tray and minimizing it must each log
AppLifecycleState.hidden.
macOS activation resumes a hidden window
- Bug:
handleWillBecomeActivein the macOS engine'sFlutterEngine.mmresumes when any window inNSApp.windowsis visible, and the tray'sNSStatusBarWindowalways is. Activating the app while its window was minimized or hidden, such as Cmd-Tab back to a minimized window, reportedresumed, and the next deactivationinactive, so frames ran until the window was shown again. - Workaround: the
windowplugin observes will-become-active and will-resign-active and, once the engine has answered, sendsAppLifecycleState.hiddenonflutter/lifecyclewhile its window is not visible (applicationActivityChangedinWindowPlugin.swift). A window brought back without another activation change would then wait on an occlusion notification macOS does not always post, keeping its last frame, soshowand a deminiaturize hand the state back (windowBecameVisible). - Remove when: the engine ignores status item windows on activation. Check without the workaround: minimize the
window, switch to another app, then Cmd-Tab back without restoring it; the log must stay at
AppLifecycleState.hidden.
Linux minimize on Wayland reads as inactive
- Bug: the Linux engine reports
hiddenforGDK_WINDOW_STATE_ICONIFIED, but xdg-shell has no minimized state, so GTK 3 never sets it on Wayland. A minimized window reported only its focus loss, asinactive, and kept rendering at the display rate (GNOME 46). - Workaround: the
windowplugin's Linuxminimizerecords the time, and a focus loss within 500 ms of it sendsAppLifecycleState.hidden. Itswindow-state-eventhandler is connected after the engine's, so that message follows the engine'sinactive; the compositor focuses the window when it restores it, which the engine reports asresumed. A minimize the compositor starts itself (Super+H, the dock) is not covered. - Remove when: the Linux engine reports minimized windows on Wayland, which needs GTK 4 or xdg-shell's
suspendedstate. Check in a GNOME Wayland session without the workaround: minimizing from the caption button must logAppLifecycleState.hidden.