Skip to content

[cupertino_ui] Fix CupertinoSheetRoute throwing when a drag ends during a pending relayout - #13316

Merged
auto-submit[bot] merged 4 commits into
flutter:mainfrom
theprantadutta:cupertino_ui/sheet-drag-size-during-layout
Nov 11, 2026
Merged

auto-submit[bot] merged 4 commits into
flutter:mainfrom
theprantadutta:cupertino_ui/sheet-drag-size-during-layout

Conversation

@theprantadutta

Copy link
Copy Markdown
Contributor

Dragging a CupertinoSheetRoute converts drag deltas and the release velocity into fractions of the sheet's height. Both drag paths read that height from context.size: _CupertinoDragGestureDetectorState.sheetHeight for regular sheets, and _dragUpdate / _handleDragEnd in _CupertinoDraggableScrollableSheetState for sheets built with scrollableBuilder. Element.size asserts when the render object is dirty for layout, so a drag that ends while a relayout is pending throws Cannot get size from a render object that has been marked dirty for layout. The reporter saw it cascade into _drag == null asserts in ScrollableState, after which the sheet stopped responding. In release the assert is stripped and the read returns the last laid out size.

The fix reads the height from the RenderBox directly. That's the size the sheet was last laid out at, which is the size the drag was made against, and it's what release builds were already using. The Material BottomSheet reads its height for drag updates and fling velocity the same way (_childHeight).

Tests

Two regression tests, one per drag path. Each starts a partial drag, marks the box the sheet measures as dirty, and releases before the next frame. Both fail on main with the assertion above, and both pass with the fix, with the sheet settling back to where it started. The rest of sheet_test.dart passes as well (44 tests in total).

One limitation: the reporter hit this on a device when the keyboard was dismissed mid-drag. Changing the view insets mid-drag in a widget test doesn't reproduce it, because the relayout that follows lands before the pointer up. So the tests set up the condition from the reporter's stack trace directly, not the keyboard sequence.

Fixes flutter/flutter#194681

Pre-Review Checklist

I used AI tooling while working on this, and reviewed and tested all of it myself.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2

…ng a pending relayout

The sheet's drag handlers read context.size to convert drag deltas and
velocities into fractions of the sheet height. Element.size asserts when
the render object is dirty for layout, so a drag that ended while a
relayout was pending threw 'Cannot get size from a render object that
has been marked dirty for layout'. Read the size from the render box
instead, which is the last laid out height and what release builds
already used.

@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 updates the sheet height retrieval in CupertinoSheetRoute to use the render box instead of context.size, preventing assertions when a drag ends during a pending relayout. Corresponding regression tests and a changelog entry are included. The review feedback suggests caching the _sheetHeight value in a local variable within _dragUpdate to avoid redundant render object lookups during drag events.

Comment on lines 1456 to 1463
void _dragUpdate(double delta) {
assert(mounted);
if (_dragGestureController != null) {
_dragGestureController!.dragUpdate(
delta / (context.size!.height - (context.size!.height * _kTopGapRatio)),
delta / (_sheetHeight - (_sheetHeight * _kTopGapRatio)),
null,
);
}

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

During a drag update, _sheetHeight is accessed twice. Since _sheetHeight performs a tree lookup via context.findRenderObject(), caching this value in a local variable avoids redundant lookups during high-frequency drag events.

Suggested change
void _dragUpdate(double delta) {
assert(mounted);
if (_dragGestureController != null) {
_dragGestureController!.dragUpdate(
delta / (context.size!.height - (context.size!.height * _kTopGapRatio)),
delta / (_sheetHeight - (_sheetHeight * _kTopGapRatio)),
null,
);
}
void _dragUpdate(double delta) {
assert(mounted);
if (_dragGestureController != null) {
final double sheetHeight = _sheetHeight;
_dragGestureController!.dragUpdate(
delta / (sheetHeight - (sheetHeight * _kTopGapRatio)),
null,
);
}
}

@Piinks
Piinks requested a review from dkwingsmt October 20, 2026 21:25

@dkwingsmt dkwingsmt 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.

LGTM except for a nit. Thank you for the fix!

Comment on lines +1043 to +1045
// Reads the height from the render box instead of context.size, which asserts
// when the box is dirty for layout. A drag can end while a relayout is
// pending, and the last laid out height is the one the drag was made against.

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.

nit: The verb "assert" is typically followed by the correct behavior, making the sentence a bit awkward to read at first glance.

Suggested change
// Reads the height from the render box instead of context.size, which asserts
// when the box is dirty for layout. A drag can end while a relayout is
// pending, and the last laid out height is the one the drag was made against.
// Reads the height from the render box instead of context.size, which throws
// in debug mode when the box is dirty for layout. A drag can end while a relayout is
// pending, and the last laid out height is the one the drag was made against.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in babcf0b, thanks!

@dkwingsmt
dkwingsmt requested a review from pjkammer November 10, 2026 07:43
}

// See _CupertinoDragGestureDetectorState.sheetHeight.
double get _sheetHeight => (context.findRenderObject()! as RenderBox).size.height;

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.

nit: It may help to extract the two methods into a global function. Non-blocking comment. Feel free to decide whether to do it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in babcf0b: both drag paths now call a top-level _sheetHeightOf(context), and each handler reads it once.

Moves the render box height read into a top-level function used by both sheet drag paths, and rewords its comment to say context.size throws in debug mode.
@dkwingsmt dkwingsmt added the CICD Run CI/CD label Nov 10, 2026
@pjkammer pjkammer added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit
auto-submit Bot merged commit 054d9d1 into flutter:main Nov 11, 2026
13 checks passed
opensource-fork-sync Bot pushed a commit to cla7aye15I4nd/opensource-flutter__flutter that referenced this pull request Nov 11, 2026
…er#196381)

flutter/packages@08faf64...653628e

2026-11-11 [email protected] Roll Flutter from
fffd8c2 to b98afb5 (31 revisions) (flutter/packages#13623)
2026-11-11 [email protected] [material_ui] Document how to
programmatically clear DropdownMenu selection (flutter/packages#13266)
2026-11-11 [email protected] [pigeon] Fix basePath resolution when
paths share a string prefix with basePath (flutter/packages#13342)
2026-11-11 [email protected]
[cupertino_ui] Fix CupertinoSheetRoute throwing when a drag ends during
a pending relayout (flutter/packages#13316)
2026-11-11 [email protected] [shared_preferences] Updates Pigeon to
30.0.1 to support multi-engine registration and deregistration.
(flutter/packages#13617)
2026-11-11 [email protected]
[vector_graphics] Catch any throwable in useHtmlRenderObject() and cache
HTML fallback (flutter/packages#13545)
2026-11-11 [email protected] [cupertino_ui]
Include selectionHandleColor in NoDefaultCupertinoThemeData equality
(flutter/packages#13450)
2026-11-11 [email protected] [flutter_svg] Remove failed async loads
from Cache._pending (flutter/packages#13604)
2026-11-11 [email protected] [pigeon] Adds Native Interop FFI codec
serialization for NS types (flutter/packages#13616)
2026-11-10 [email protected] [pigeon] Support multi-engine Native
Interop registration and instance-aware unregistration
(flutter/packages#13576)
2026-11-10 [email protected] [image_picker_ios] Convert the
iOS example app and UI tests to Swift (flutter/packages#13570)
2026-11-10 [email protected]
[cupertino_ui] Localize CupertinoActionSheet semantics label
(flutter/packages#13377)
2026-11-10 [email protected] [camera_android]
Fix stopVideoRecording returning JPEG when takePicture runs during
recording (flutter/packages#13070)
2026-11-10 [email protected] [google_sign_in_android] Rename keep.xml
to google_sign_in_keep.xml (flutter/packages#13475)
2026-11-10 [email protected] [web_benchmarks] Fix benchmark stalling
when only repainting views that are dirty (flutter/packages#13089)
2026-11-10 [email protected] Roll Flutter from
d463821 to fffd8c2 (21 revisions) (flutter/packages#13614)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC [email protected] on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: cupertino_ui triage-design Should be looked at in design triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CupertinoSheetRoute asserts when a drag ends in the frame a relayout lands

3 participants