Skip to content

Prevent console from auto-scrolling to bottom#9008

Open
mjiang97 wants to merge 1 commit into
flutter:mainfrom
mjiang97:fix-issue-3778
Open

Prevent console from auto-scrolling to bottom#9008
mjiang97 wants to merge 1 commit into
flutter:mainfrom
mjiang97:fix-issue-3778

Conversation

@mjiang97

@mjiang97 mjiang97 commented Jun 22, 2026

Copy link
Copy Markdown

PR Description

Scroll the run console to the first of multiple errors instead of auto-scrolling to the bottom.

When a Flutter app throws a cascade of errors (e.g. an unbounded viewport), the logging console floods with hundreds of lines and auto-scrolls to the bottom, burying the root-cause error at the top. The reporter had to manually scroll back up to find it.

This changes DaemonConsoleView so that when the first error of a burst arrives (detected either from error-stream output or from the Flutter error banner Exception caught by …), the console scrolls that first error to the top of the viewport and freezes auto-follow. The root-cause error stays visible while the rest of the stack traces stream in below. Normal auto-follow resumes once the user scrolls back to the bottom.

Details:

  • Reading editor geometry to determine scroll position happens only on the EDT (scheduled via invokeLater, throttled with an AtomicBoolean) to respect the platform threading model.
  • A guard prevents the transient "at end" produced by our own pin-scroll (on a still-short document) from releasing the pin, so focus stays on the first error rather than a later one.
  • If the user is still viewing the pinned error, a subsequent error cascade does not steal focus; following (and re-pinning to a new cascade's first error) resumes only after they scroll back to the bottom.
  • Console state resets on clear() (hot reload/restart).

Fixes #3778

To verify:

  1. Run the reproduction app: https://github.com/InMatrix/flutter_error_studies
  2. Tap "Unbounded Viewport"
  3. Before: console jumps to the bottom showing hasSize assertion failures
  4. After: console holds on "Vertical viewport was given unbounded height" (the root cause). Scroll to the bottom to resume normal following.

A before/after demo video is attached in the comments.


Review the contribution guidelines below:

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
  • I've included the required information in the description above.
  • My up-to-date information is in the AUTHORS file.
  • I've updated CHANGELOG.md if appropriate.
Contribution guidelines:
  • See
    our contributor guide and
    the Flutter organization contributor guide
    for general expectations for PRs.
  • Larger or significant changes should be discussed in an issue before creating a PR.
  • Dart contributions to our repos should follow the Dart style guide and use
    dart format.
  • Java and Kotlin contributions should strive to follow Java and Kotlin best
    practices (discussion).

@google-cla

google-cla Bot commented Jun 22, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mechanism in DaemonConsoleView to preserve the user's scroll position when new console output is printed, accompanied by unit tests for the scroll-end detection logic. The review feedback highlights a critical threading violation where UI components and editor geometry are accessed outside the Event Dispatch Thread (EDT). To resolve this, the reviewer suggests scheduling the scroll check on the EDT and throttling the requests using an AtomicBoolean to prevent performance degradation during rapid output.

Comment on lines +82 to +86
final Editor rawEditor = getEditor();
final EditorEx editor = rawEditor instanceof EditorEx ? (EditorEx) rawEditor : null;
if (editor != null) {
wasAtScrollEnd = isAtScrollEnd(editor);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

[MUST-FIX] Accessing the Editor and its ScrollingModel (via getEditor() and isAtScrollEnd(editor)) from a background thread violates the IntelliJ Platform threading model. UI components and editor geometry must only be accessed on the Event Dispatch Thread (EDT). Doing otherwise can lead to AssertionErrors or race conditions.

We can safely schedule this check on the EDT using ApplicationManager.getApplication().invokeLater(...), throttled by the scrollCheckPending flag to avoid performance degradation during rapid output.

    if (com.intellij.openapi.application.ApplicationManager.getApplication().isDispatchThread()) {
      final Editor rawEditor = getEditor();
      final EditorEx editor = rawEditor instanceof EditorEx ? (EditorEx) rawEditor : null;
      if (editor != null) {
        wasAtScrollEnd = isAtScrollEnd(editor);
      }
    } else if (scrollCheckPending.compareAndSet(false, true)) {
      com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater(() -> {
        scrollCheckPending.set(false);
        final Editor rawEditor = getEditor();
        final EditorEx editor = rawEditor instanceof EditorEx ? (EditorEx) rawEditor : null;
        if (editor != null) {
          wasAtScrollEnd = isAtScrollEnd(editor);
        }
      }, com.intellij.openapi.application.ModalityState.any());
    }

Comment on lines +62 to +64
// Whether the console was scrolled to the end before the most recent batch of output.
// Defaults to true so that the first output auto-scrolls normally.
private boolean wasAtScrollEnd = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

[CONCERN] To support thread-safe scheduling of the scroll position check from background threads, we should introduce a throttled state indicator using an AtomicBoolean. This prevents flooding the Event Dispatch Thread (EDT) with redundant runnables when console output is rapid.

Suggested change
// Whether the console was scrolled to the end before the most recent batch of output.
// Defaults to true so that the first output auto-scrolls normally.
private boolean wasAtScrollEnd = true;
// Whether the console was scrolled to the end before the most recent batch of output.
// Defaults to true so that the first output auto-scrolls normally.
private boolean wasAtScrollEnd = true;
private final java.util.concurrent.atomic.AtomicBoolean scrollCheckPending = new java.util.concurrent.atomic.AtomicBoolean(false);

@helin24

helin24 commented Jun 22, 2026

Copy link
Copy Markdown
Member

Sorry I didn't reply in the issue; I didn't see your message there until now! It looks like you were able to find the relevant file to make these changes anyway; thanks!

I think the gemini comments above would be good to address. A gif/video of before and after would also be helpful.

What happens (or should happen) if a user triggers an event that produces a cascade of errors, is looking at the root cause error at the top, and then triggers another event that produces errors? Will they just stay at the first error?

@mjiang97

mjiang97 commented Jul 6, 2026

Copy link
Copy Markdown
Author

Thanks @helin24 for the suggestions!

I have addressed Gemini's comments above and amended the original commit with those changes. I have also attached the before/after videos.

As for your question about the expected behavior, after my changes the user should be able to see the root cause error at the top and remain at the top even if another event produces more errors. I did some testing and this behavior is consistent across all errors, so you should be able to see the scroll button move up as the errors cascade and accumulate below, but the user will stay where they are in the console.

Before changes demo
After changes demo

@helin24

helin24 commented Jul 8, 2026

Copy link
Copy Markdown
Member

I should have thought of this before, but I think we should make this an option that's added to settings, so that it is only enabled if someone wants to use this smart error scrolling. What do you think of that? I think it's going to be a matter of preference, where some users will prefer the window stay on the first error and some will want to see the bottom of the output. CC @pq if you have other thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop scrolling the console to the bottom when multiple errors are shown

2 participants