Repository navigation
Description
Steps to reproduce
flutter create bugand replacelib/main.dartwith the sample below.flutter run -d macos. Reproduces in debug, profile and release.- Drag any window edge or corner to resize.
- Flip
kPaintSomethingtotrueand repeat step 3.
Expected results
Resizing is smooth in both cases. A frame in which nothing paints should not make window resizing slower than a frame that paints something.
Actual results
With kPaintSomething = false the window follows the cursor at roughly one step per second and the console fills with Resize timed out. With kPaintSomething = true — a single opaque box, nothing else changed — resizing is smooth.
| scene | mean per resize step | Resize timed out |
|---|---|---|
kPaintSomething = false (nothing paints) |
1083.67 ms | 20 / 20 |
kPaintSomething = true (one opaque box) |
16.37 ms | 0 |
A release build behaves the same as debug: 1093.41 ms per step, 20/20 timed out. 1083 ms is the ResizeSynchronizer timeout, so every step times out.
The timings come from local instrumentation, not from the Dart sample — the probe is included below so the measurement is reproducible. The user-visible symptom (one resize step per second, Resize timed out spam) needs only the Dart sample and a mouse.
Suspected cause
Not maintainer-confirmed; this is my reading of the engine source at the revision under test (83675ed276).
GetRequiredFrameSize() derives the committed frame size from the presented surfaces, starting at CGSizeZero:
static CGSize GetRequiredFrameSize(NSArray<FlutterSurfacePresentInfo*>* surfaces) {
CGSize size = CGSizeZero;
for (FlutterSurfacePresentInfo* info in surfaces) { ... }
return size;
}When the scene paints nothing, EmbedderExternalView::HasEngineRenderedContents() is false — DlOpSpy::setColor treats a fully transparent colour as "will not draw" — so FlutterCompositor::Present builds an empty surfaces array and the committed size is (0, 0). ResizeSynchronizer.beginResize is waiting for contentSize == size, which can never happen, so -[FlutterView setFrameSize:] spins the platform thread for the full timeoutDuration = 1.0.
For contrast, the Windows embedder completes the same handshake from the resize target rather than from the presented layers (FlutterWindowsView::OnEmptyFrameGenerated, regression test TestEmptyFrameResizes), so an empty frame there does not stall.
A real app reaches this state by rendering into a native CALayer inserted below a transparent FlutterView — with the overlay UI hidden, nothing in the Flutter scene paints and window resizing becomes unusable. I have not checked whether a scene containing only a platform view (and no engine-rendered content) hits the same path.
Code sample
Code sample
import 'package:flutter/material.dart';
/// Flip to `true` and the resize stall disappears.
const bool kPaintSomething = false;
void main() {
runApp(
MaterialApp(
// The debug banner paints, which would defeat the repro.
debugShowCheckedModeBanner: false,
home: Scaffold(
// Nothing in the scene paints, so the frame has no backing store.
// A real app reaches this state by drawing its content into a native
// CALayer inserted below a transparent FlutterView.
backgroundColor: Colors.transparent,
body: kPaintSomething
? const ColoredBox(color: Color(0xFF2196F3), child: SizedBox.expand())
: const SizedBox.expand(),
),
),
);
}Measurement probe (optional — appended to macos/Runner/MainFlutterWindow.swift)
Call ResizeProbe.install(window: self) at the end of awakeFromNib(), then kill -USR1 <pid>. It drives 20 setFrame steps through the same -[FlutterView setFrameSize:] path a live drag uses and reports how long each one blocked the main thread.
enum ResizeProbe {
private static var source: DispatchSourceSignal?
static func install(window: NSWindow) {
signal(SIGUSR1, SIG_IGN)
let src = DispatchSource.makeSignalSource(signal: SIGUSR1, queue: .main)
src.setEventHandler { [weak window] in
guard let window else { return }
let base = window.frame
var samples: [Double] = []
for i in 0..<20 {
var f = window.frame
let d = CGFloat((i % 2 == 0) ? 6 : -6)
f.size.width += d; f.size.height += d
let t0 = CFAbsoluteTimeGetCurrent()
window.setFrame(f, display: true)
samples.append((CFAbsoluteTimeGetCurrent() - t0) * 1000.0)
}
window.setFrame(base, display: true)
let mean = samples.reduce(0, +) / Double(samples.count)
NSLog("[ResizeProbe] n=%d mean=%.2fms", samples.count, mean)
}
src.resume(); source = src
}
}Logs
Logs
Running with merged UI and platform thread. Experimental.
Resize timed out
Resize timed out
Resize timed out
Resize timed out
... (one per resize step; 20 of 20 in the measured run)
[ResizeProbe] n=20 mean=1083.67msFlutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.44.5, on macOS 26.5.2 25F84 darwin-arm64, locale en-US) [215ms]
• Flutter version 3.44.5 on channel stable at ~/Documents/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision f94f4fc76b (3 weeks ago), 2026-07-06 11:19:24 -0700
• Engine revision 83675ed276
• Dart version 3.12.2
• DevTools version 2.57.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
[!] Android toolchain - develop for Android devices (Android SDK version 37.0.0) [975ms]
• Android SDK at ~/Library/Android/sdk
• Emulator version 36.6.11.0 (build_id 15507667) (CL:N/A)
• Platform android-37.0, build-tools 37.0.0
• ANDROID_HOME = ~/Library/Android/sdk
• ANDROID_SDK_ROOT = ~/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 21.0.10+-117844308-b1163.108)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 26.6) [528ms]
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 17F113
• CocoaPods version 1.17.0
[✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) [3ms]
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Connected device (4 available) [6.0s]
• Pixel 7 (mobile) • <redacted-serial> • android-arm64 • Android 16 (API 36)
• Box R 4K Plus (mobile) • <redacted-lan-device> • android-arm • Android 14 (API 34)
• iPhone 17 (mobile) • <redacted-simulator-uuid> • ios • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator)
• macOS (desktop) • macos • darwin-arm64 • macOS 26.5.2 25F84 darwin-arm64
[✓] Network resources [639ms]
• All expected network resources are available.
! Doctor found issues in 2 categories.Searched for duplicates
GetRequiredFrameSize (0 results), CGSizeZero resize (0), ResizeSynchronizer (10, none about zero-layer frames), "Resize timed out" (1: #188493, closed as a duplicate of #155977 — a permanent freeze after app switching, not a per-step resize stall). #104638 covers app code blocking the main queue, which is the opposite direction. GetRequiredFrameSize is unchanged on main as of be9b204 (2026-07-27).
Activity
saurabh-mirajkar commentedon Jul 28, 2026
@edde746 Thanks for the detailed report. I can reproduce this with the sample provided on the latest stable Flutter release on macOS in debug, profile, and release modes when
kPaintSomething = false. Labelling this as reproducible and routing it for further investigation.nyinyi740022900-stack commentedon Jul 28, 2026
Possibly related, but distinct enough to be worth noting: I'm seeing a permanent version of this (not just a resize stall) on a real production app, from the very first launch — no resize interaction involved at all.
Setup:
flutter create --platforms=macos,windows .on an existing Flutter app (offline-first POS, Material 3, no customCALayers, no transparentScaffoldbackgrounds — a fully opaque UI throughout).Symptom:
flutter run -d macos(andflutter build macos && open build/.../Runner.app) launches a realNSWindow— it shows up in the app'sWindowmenu, the process stays alive with near-zero CPU, no crash, no exception in the console — but the window never paints any content, in debug, profile, or release. Confirmed independently by a human looking at the physical screen and by automated screenshots (so it's not a screen-capture/compositor artifact). The console does printRunning with merged UI and platform thread. Experimental.at launch.Ruled out:
system_profiler SPDisplaysDataTypereports full Metal 4 support (Apple M4).MainFlutterWindow.swiftcustomizations — reproduces with the vanillaflutter create-generated window code too, and with an explicitsetFrame(..., display: true)call removed entirely fromawakeFromNib(tried both, same result — removing it entirely even seems to drop the window'sWindowmenu entry, suggesting the content view controller attachment path is itself affected).3.44.5/3.44.8) and on3.41.0(~6 months older, installed via FVM), both printing the samemerged UI and platform thread. Experimental.line.Environment: macOS 26.5.1 (25F80), Apple M4, engine hash
83675ed276(matches the revision referenced in this issue).Given the resize-stall root cause described here — an empty/zero-content first frame permanently wedging
ResizeSynchronizer'sbeginResize/contentSize == sizehandshake — it seems plausible a similar "frame committed as empty" state could be reached from a different path than the one described (transparentScaffold+ customCALayer) and then never recover, even without any user resize interaction, if the very first frame race loses for any reason. I haven't been able to pin down what makes the very first frame register as "nothing to rasterize" in my case (the UI is fully opaque throughout), so I can't rule out this being a separate bug that only looks similar from the outside — flagging it here in case it's useful corroborating data, or in case a maintainer recognizes the pattern.Happy to provide a minimal reproduction repo if useful.
LouiseHsu commentedon Jul 31, 2026
Thank you for the detailed investigation - we are happy to take contributions! :)