You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a context menu (toolbar) is visible and the user performs a short, non-fling scroll that keeps the selection in view, the toolbar does not re-appear after the scroll ends. A subsequent scroll causes the toolbar to appear at the beginning and remain visible throughout the entire scroll, overlapping UI elements like AppBars.
Steps to reproduce
Open an app with a scrollable TextField containing enough text to scroll (e.g., TextField(maxLines: 5, controller: TextEditingController(text: 'Hello world! ' * 200)))
Long press to select a word and show the context menu
Perform a very short, slow scroll (non-fling) — just enough to move the content slightly while keeping the selection in view
Release the gesture
Expected:
The toolbar re-appears after the scroll ends since the selection is still in view.
Screen.Recording.2026-04-14.at.3.46.01.PM.mov
Actual:
The toolbar stays hidden. A second scroll causes the toolbar to appear at the start of the scroll and remain visible throughout, never hiding.
Screen.Recording.2026-04-14.at.3.42.55.PM.mov
Root cause
In _handleContextMenuOnScroll, when a ScrollEndNotification is received, a callback is registered via addPostFrameCallback to re-show the toolbar if the selection is still in view. However, addPostFrameCallback does not call scheduleFrame(). If no other mechanism independently schedules a frame, hasScheduledFrame remains false and the callback never fires.
On device without accessibility services, the only other candidate for scheduling a frame during the scroll-end transition is setIgnorePointer in ScrollPosition.beginActivity, which calls markNeedsSemanticsUpdate() on the RenderIgnorePointer. But markNeedsSemanticsUpdate() early-returns without scheduling a frame when no SemanticsOwner exists — which is the case on any device without a screen reader active:
// In RenderObject.markNeedsSemanticsUpdate():
if (!attached || owner!._semanticsOwner ==null) {
_needsSemanticsUpdate =true;
return; // No frame scheduled
}
This means after a non-fling scroll (where no ballistic simulation drives further frames), no frame is ever scheduled, and the addPostFrameCallback callback sits pending indefinitely.
Description
When a context menu (toolbar) is visible and the user performs a short, non-fling scroll that keeps the selection in view, the toolbar does not re-appear after the scroll ends. A subsequent scroll causes the toolbar to appear at the beginning and remain visible throughout the entire scroll, overlapping UI elements like AppBars.
Steps to reproduce
Expected:
The toolbar re-appears after the scroll ends since the selection is still in view.
Screen.Recording.2026-04-14.at.3.46.01.PM.mov
Actual:
The toolbar stays hidden. A second scroll causes the toolbar to appear at the start of the scroll and remain visible throughout, never hiding.
Screen.Recording.2026-04-14.at.3.42.55.PM.mov
Root cause
In _handleContextMenuOnScroll, when a ScrollEndNotification is received, a callback is registered via addPostFrameCallback to re-show the toolbar if the selection is still in view. However, addPostFrameCallback does not call scheduleFrame(). If no other mechanism independently schedules a frame, hasScheduledFrame remains false and the callback never fires.
On device without accessibility services, the only other candidate for scheduling a frame during the scroll-end transition is setIgnorePointer in ScrollPosition.beginActivity, which calls markNeedsSemanticsUpdate() on the RenderIgnorePointer. But markNeedsSemanticsUpdate() early-returns without scheduling a frame when no SemanticsOwner exists — which is the case on any device without a screen reader active:
// In RenderObject.markNeedsSemanticsUpdate():
This means after a non-fling scroll (where no ballistic simulation drives further frames), no frame is ever scheduled, and the addPostFrameCallback callback sits pending indefinitely.