-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Description
Steps to reproduce
I encountered a regression in Flutter 3.41.0 (Web/WASM) where the background flashes white/transparent during resize.
Investigation Summary: The issue seems to be caused by the architectural changes in the rasterization pipeline:
Async Preparation: prepareToDraw is now async (await), introducing a gap between the resize event and the actual draw.
Explicit Clear: CkSurface.setSize calls _canvasProvider.resizeCanvas, which clears the HTML Canvas content (W3C spec) before the new frame is ready.
Missing Blit: There is no mechanism to preserve the previous frame's bitmap during this async gap.
Reproduction: Resize any app with a heavy widget (like a glass effect) on Web/WASM. The underlying Scaffold background (white) is exposed.
Workaround: I had to decouple the static background into a separate DOM layer (Stack bottom) to persist visibility during the canvas clear.
Please consider implementing a frame preservation mechanism during the setSize operation.
Detailed as below:
I am reporting a regression in Flutter 3.41.0 (Web/WASM) where the Scaffold background flashes white (or transparent) during window resize. This issue was not present in 3.38.9.
I have conducted a deep-dive investigation into the engine source code changes between 3.38.9 and 3.41.0. The root cause appears to be a specific combination of the new async rasterization pipeline and the HTML Canvas clearing behavior.
Below is the detailed technical analysis:
Technical Investigation Report
The white flash is caused by the Web Compositing pipeline re-architecture in 3.41.0. Specifically, it is the result of four compounding changes that leave the HTML Canvas in a cleared state during an asynchronous gap.
- prepareToDraw changed from Sync to Async
In 3.38.9, prepareToDraw was synchronous, allowing the frame to start drawing immediately. In 3.41.0, it has become asynchronous, introducing a delay before drawing begins.
3.38.9 (offscreen_canvas_rasterizer.dart):
@override
void prepareToDraw() {
rasterizer.offscreenSurface.createOrUpdateSurface(currentFrameSize);
}
3.41.0 (multi_surface_rasterizer.dart):
@override
Future<void> prepareToDraw() async {
await rasterizer.offscreenSurface.setSize(currentFrameSize);
}
Impact: This await introduces at least one microtask tick of latency. During this gap, the canvas state changes described in point #2 become visible to the user.
CkSurface.setSizetriggers implicit Canvas Clear
In 3.41.0, the new setSize implementation directly modifies the canvas dimensions, which triggers a browser-level clear.
3.41.0 (surface.dart):
@override
void setSize(BitmapSize size) {
// ... check if size changed ...
_canvasProvider.resizeCanvas(canvas, size); // <--- DESTRUCTIVE OPERATION
_recreateSkSurface();
}
The Issue: _canvasProvider.resizeCanvas updates the HTML Canvas width and height attributes. Per the W3C HTML specification, setting these attributes clears the canvas content to transparent black.
Contrast with 3.38.9: The previous version's createOrUpdateSurface logic (specifically _createNewCanvas paths) handled surface updates without exposing this cleared state to the user, likely by swapping DOM elements or retaining the previous surface until the new one was ready.
- Increased Latency in Rasterization Pipeline
The rasterization flow in 3.41.0 now involves multiple await steps that were previously synchronous or atomic:
prepareToDraw() (await)
setSize() -> Canvas is cleared here
rasterizeToImageBitmaps() (await)
rasterizeToCanvas() (await)
Because the Canvas is cleared at step 2, and the actual drawing doesn't happen until step 4, there is a visible window where the browser paints an empty (white/transparent) frame.
- Aggressive Metrics Firing via ResizeObserver
The mechanism for detecting layout metrics changes has been made significantly more sensitive, triggering the above cycle more frequently.
3.38.9: Used DomMutationObserver watching only the style attribute of .
3.41.0: Uses DomResizeObserver watching an offscreen
element (_typographyMeasurementElement).
This observer fires on a wider range of changes (font loading, zoom levels, minor layout shifts), invoking invokeOnMetricsChanged() more often. This causes the framework to request new frames and trigger the destructive setSize flow more frequently during resize operations.
Conclusion & Suggestion
The issue is not just a performance regression but a visual correctness regression. The implementation of CkSurface.setSize in 3.41.0 lacks a mechanism to preserve the previous frame's content while the new surface is being prepared asynchronously.
Suggested Fix: Before calling _canvasProvider.resizeCanvas (which clears the canvas), the engine should ideally copy the current canvas content (e.g., to a temporary canvas or bitmap) and draw it back immediately after resize, or employ a double-buffering strategy at the DOM level to hide the cleared canvas until the new frame is fully rasterized.
Expected results
The application should resize smoothly. The content of the previous frame (or a stretched version of it) should remain visible until the new frame is fully rasterized and ready to be painted. There should be no visual gaps or flickering.
Actual results
The application canvas flashes white (or transparent) repeatedly during the resize drag operation. The underlying HTML page background is momentarily exposed because the Canvas element is explicitly cleared before the new frame is drawn.
Code sample
Technical Investigation Report
The white flash is caused by the Web Compositing pipeline re-architecture in 3.41.0. Specifically, it is the result of four compounding changes that leave the HTML Canvas in a cleared state during an asynchronous gap.
prepareToDrawchanged from Sync to Async
In 3.38.9,prepareToDrawwas synchronous, allowing the frame to start drawing immediately. In 3.41.0, it has become asynchronous, introducing a delay before drawing begins.
3.38.9 (offscreen_canvas_rasterizer.dart):
@override
void prepareToDraw() {
rasterizer.offscreenSurface.createOrUpdateSurface(currentFrameSize);
}
3.41.0 (multi_surface_rasterizer.dart):
@override
Future<void> prepareToDraw() async {
await rasterizer.offscreenSurface.setSize(currentFrameSize);
}
Impact: This await introduces at least one microtask tick of latency. During this gap, the canvas state changes described in point #2 become visible to the user.
CkSurface.setSizetriggers implicit Canvas Clear
In 3.41.0, the new setSize implementation directly modifies the canvas dimensions, which triggers a browser-level clear.
3.41.0 (surface.dart):
@override
void setSize(BitmapSize size) {
// ... check if size changed ...
_canvasProvider.resizeCanvas(canvas, size); // <--- DESTRUCTIVE OPERATION
_recreateSkSurface();
}
The Issue: _canvasProvider.resizeCanvas updates the HTML Canvas width and height attributes. Per the W3C HTML specification, setting these attributes clears the canvas content to transparent black.
Contrast with 3.38.9: The previous version's createOrUpdateSurface logic (specifically _createNewCanvas paths) handled surface updates without exposing this cleared state to the user, likely by swapping DOM elements or retaining the previous surface until the new one was ready.
- Increased Latency in Rasterization Pipeline
The rasterization flow in 3.41.0 now involves multiple await steps that were previously synchronous or atomic:
prepareToDraw() (await)
setSize() -> Canvas is cleared here
rasterizeToImageBitmaps() (await)
rasterizeToCanvas() (await)
Because the Canvas is cleared at step 2, and the actual drawing doesn't happen until step 4, there is a visible window where the browser paints an empty (white/transparent) frame.
- Aggressive Metrics Firing via ResizeObserver
The mechanism for detecting layout metrics changes has been made significantly more sensitive, triggering the above cycle more frequently.
3.38.9: Used DomMutationObserver watching only the style attribute of .
3.41.0: Uses DomResizeObserver watching an offscreen
element (_typographyMeasurementElement).
This observer fires on a wider range of changes (font loading, zoom levels, minor layout shifts), invoking invokeOnMetricsChanged() more often. This causes the framework to request new frames and trigger the destructive setSize flow more frequently during resize operations.
Conclusion & Suggestion
The issue is not just a performance regression but a visual correctness regression. The implementation of CkSurface.setSize in 3.41.0 lacks a mechanism to preserve the previous frame's content while the new surface is being prepared asynchronously.
Suggested Fix: Before calling _canvasProvider.resizeCanvas (which clears the canvas), the engine should ideally copy the current canvas content (e.g., to a temporary canvas or bitmap) and draw it back immediately after resize, or employ a double-buffering strategy at the DOM level to hide the cleared canvas until the new frame is fully rasterized.
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[2026/02/13 14:45:28] (121s) /c/0/w/a/sr_v3_3 >>>fvm flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.41.0, on Microsoft Windows [Version
10.0.26200.7705], locale ja-JP)
[√] Windows Version (11 Pro 64-bit, 25H2, 2009)
[X] Android toolchain - develop for Android devices
X Unable to locate Android SDK.
Install Android Studio from:
https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android
SDK components.
(or visit https://flutter.dev/to/windows-android-setup for
detailed instructions).
If the Android SDK has been installed to a custom location,
please use
`flutter config --android-sdk` to update to that location.
[√] Chrome - develop for the web
[!] Visual Studio - develop Windows apps (Visual Studio Build
Tools 2022 17.14.15 (September 2025))
X Visual Studio is missing necessary components. Please re-run
the Visual Studio installer for the "Desktop development
with C++" workload, and include these components:
MSVC v142 - VS 2019 C++ x64/x86 build tools
- If there are multiple build tool versions available,
install the latest
C++ CMake tools for Windows
Windows 10 SDK
[√] Connected device (3 available)
[√] Network resources
! Doctor found issues in 2 categories.
[2026/02/13 15:45:12] (5s) /c/0/w/a/sr_v3_3 >>>