fix(ObserverController): correct the scrolling clamped by an outdated scrollExtent - #151
Merged
Merged
Conversation
… scrollExtent A RenderSliverMultiBoxAdaptor may skip its layout phase when the number of children changes but none of the existing children needs to be laid out again, which leaves its SliverGeometry.scrollExtent outdated. The target offset is then clamped to the outdated ScrollPosition.maxScrollExtent, so the target child widget cannot be reached until scrolling to index a second time. The scrolling itself changes ScrollPosition.pixels, which makes the sliver be laid out again and report a fresh scrollExtent in the next frame. So expose isEnoughScroll from ObservePrepareScrollToIndexModel to tell whether the scrolling has been clamped, then recalculate and scroll again until the target offset no longer changes. It is applied to the two one-shot paths, the fixed height one and the one hitting the indexOffsetMap cache. The path of gradually scrolling around the target index location already converges by itself. Closes #150
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issues
Description
Scrolling to an index right after the item count changed lands on the wrong
offset. Scrolling a second time works.
Root cause.
SliverMultiBoxAdaptorElement.performRebuild()only updatesexisting children. When the item count changes but every existing child rebuilds
into an identical widget, nothing calls
markNeedsLayout(), so the sliver skipsits layout phase and
SliverGeometry.scrollExtentkeeps the value estimated forthe old item count. The framework guards against this only when
_didUnderflowis true, which is not the case here.
maxScrollExtentis therefore outdated and_calculateTargetLayoutOffsetclamps the target offset to it:This also affects
ListView, and only the paths that scroll once.isFixedHeight: falsegoes through_handleScrollToIndex, which re-reads thelayout every round and recovers by itself.
Fix. The scrolling itself changes
ScrollPosition.pixels, so the sliver islaid out again and reports a fresh
scrollExtentin the next frame. That is whyscrolling twice works, and the package can do the second one itself.
isEnoughScrollfromObservePrepareScrollToIndexModel. It was alreadycomputed and thrown away, and means "the target offset has been clamped".
_scrollToWithCorrection(): while the scrolling was clamped, wait for theend of the frame, recalculate and scroll again, until the target offset no
longer changes (or 5 corrections, or the render object is gone).
_handleScrollToIndexForFixedHeight()andthe
indexOffsetMapcache branch of_scrollToIndex().Forcing a relayout up front instead would be smaller, but
RenderSliverListextrapolates its extent, so
ListView.separatedstill lands 21~38px short on 4of 24 item/separator height combinations. The loop converges on all of them and
only costs extra frames when the scrolling was actually clamped.
Tests
New group
Scroll to index after the item count changed, one case ingrid_observer_test.dartand two inlist_observer_test.dart(plain andseparated). Each asserts that
maxScrollExtentis still outdated right after therebuild, so the tests fail loudly if the framework ever changes this behaviour.
Verified to fail without the fix.
flutter test- 83 passedflutter analyze- no issues inlib/andtest/dart format --set-exit-if-changed lib/ test/