Skip to content

Conversation

@yiiim
Copy link
Member

@yiiim yiiim commented Nov 26, 2025

Background: #173857.

ScrollMetricsNotification was originally not intended to be triggered during scrolling, but CustomScrollView triggers a layout during scrolling, which also triggers ScrollMetricsNotification. SingleChildScrollView does not trigger a layout when scrolling, resulting in different behaviors between SingleChildScrollView and CustomScrollView. This PR unifies their behavior so that both trigger ScrollMetricsNotification when scrolling.

@chunhtai, this breaks some of your tests in #87076, mainly because triggering ScrollMetricsNotification during scrolling happens one frame earlier than during layout, resulting in one less frame. Is this correct?

The original purpose was to update _lastMetrics. I tried three approaches:

  1. As shown in this PR, trigger ScrollMetricsNotification and update _lastMetrics on every scroll;

  2. Cancel triggering ScrollMetricsNotification during CustomScrollView scrolling, keep applyContentDimensions unchanged, and update only the pixels property in _lastMetrics during scrolling without sending notifications. Since pixels is already updated, applyContentDimensions will not trigger notifications either. This will cause more test failures.

  3. Maintain the current ScrollMetricsNotification triggering, similar to Fix ScrollMetricsNotifiation for SingleChildScrollView #102339.

I prefer the first approach, which keeps the behavior of SingleChildScrollView and CustomScrollView consistent.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@yiiim yiiim marked this pull request as draft November 26, 2025 12:53
@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: scrolling Viewports, list views, slivers, etc. labels Nov 26, 2025
Copy link
Contributor

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

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 modifies the notifyListeners method in ScrollPosition to update the _lastMetrics field with the current scroll pixels value. While this correctly addresses the issue of stale pixel data, I have a concern regarding the performance impact of creating a new ScrollMetrics object on every frame during scroll animations. I've provided a comment suggesting a more performant alternative.

Comment on lines 1123 to 1125
_lastMetrics = _lastMetrics?.copyWith(
pixels: pixels,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

While this change correctly updates the pixels value, it does so on every frame during a scroll animation by creating a new FixedScrollMetrics object. This could have performance implications in complex scrolling scenarios.

A more performant approach would be to update the metrics only when they are about to be dispatched. For example, if _lastMetrics is used in didEndScroll, the copyWith call could be moved there. This would avoid object creation on every frame of a scroll.

Consider removing this change and applying the fix closer to where _lastMetrics is consumed, for instance:

// In didEndScroll()
if (_lastMetrics != null) {
  final ScrollMetrics endMetrics = _lastMetrics!.copyWith(pixels: pixels);
  ScrollMetricsNotification(metrics: endMetrics).dispatch(context.notificationContext);
}

This would achieve the same goal with less overhead during active scrolling.

@yiiim yiiim force-pushed the fixes/scroll_position branch 3 times, most recently from 67b7bc8 to 3e85619 Compare November 28, 2025 05:45
@yiiim yiiim changed the title WIP Send ScrollMetricsNotification when SingleChildScrollView is scrolled. Nov 29, 2025
@yiiim yiiim requested a review from chunhtai November 29, 2025 04:28
@yiiim yiiim marked this pull request as ready for review November 29, 2025 04:28
expect(events[2] is ScrollUpdateNotification, true);
expect(events[3] is ScrollEndNotification, true);
expect(events[4] is UserScrollNotification, true);
expect(events[3] is ScrollMetricsNotification, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

I just catch this now, but if only pixel (scroll position) is changing shouldn't this be ScrollUpdateNotification ? in that case line 51 has already included that, do we need to fire another ScrollMetricsNotification?

Copy link
Member Author

@yiiim yiiim Dec 2, 2025

Choose a reason for hiding this comment

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

This is the tricky part. For a CustomScrollView, every scroll triggers a relayout, so it dispatches a ScrollMetricsNotification, but this is not the case for a SingleChildScrollView. My idea is to make them consistent. I think the most ideal way is to not dispatch ScrollMetricsNotification on scroll, but this would be a breaking change for CustomScrollView. Alternatively, we could maintain the status quo and use the reopened #102339 to update _lastMetrics. @Piinks, do you remember why this PR was closed at the time?

The solution in this PR is to have SingleChildScrollView also dispatch a ScrollMetricsNotification on every scroll. As the PR description says, this also breaks some tests. I'd like to know which solution you think we should adopt? @chunhtai @Piinks.

@yiiim yiiim requested a review from Piinks December 2, 2025 01:39
@Piinks
Copy link
Contributor

Piinks commented Dec 2, 2025

ScrollMetricsNotification was originally not intended to be triggered during scrolling, but CustomScrollView triggers a layout during scrolling, which also triggers ScrollMetricsNotification.

Is this always true? Or only when the metrics have changed?

@Piinks
Copy link
Contributor

Piinks commented Dec 2, 2025

This PR unifies their behavior so that both trigger ScrollMetricsNotification when scrolling.

Why do we need to unify the behavior? What I am wondering here considering this change is, what is the problem this is trying to solve? Looking at the older linked issues, these are not meant to behave the same way.

@yiiim yiiim marked this pull request as draft December 8, 2025 02:06
@flutter-dashboard
Copy link

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@yiiim
Copy link
Member Author

yiiim commented Dec 12, 2025

ScrollMetricsNotification was originally not intended to be triggered during scrolling, but CustomScrollView triggers a layout during scrolling, which also triggers ScrollMetricsNotification.

Is this always true? Or only when the metrics have changed?

When a CustomScrollView scrolls, it inevitably triggers a layout pass, which calls applyContentDimensions. In applyContentDimensions, as shown here:

, since pixels has already changed, _isMetricsChanged() is always true. Therefore, scrolling a CustomScrollView will always trigger a ScrollMetricsNotification.

@yiiim
Copy link
Member Author

yiiim commented Dec 12, 2025

This PR unifies their behavior so that both trigger ScrollMetricsNotification when scrolling.

Why do we need to unify the behavior? What I am wondering here considering this change is, what is the problem this is trying to solve? Looking at the older linked issues, these are not meant to behave the same way.

The issue we need to address is updating _lastMetrics for SingleChildScrollView, which is a prerequisite for resolving #173857. I considered keeping the current inconsistent behavior. However, if we update _lastMetrics, it will inevitably trigger a ScrollMetricsNotification.

@yiiim yiiim force-pushed the fixes/scroll_position branch from a9b771e to 286ef4f Compare December 12, 2025 04:09
@yiiim yiiim force-pushed the fixes/scroll_position branch from 373f708 to 9568a62 Compare December 12, 2025 05:37
@yiiim yiiim marked this pull request as ready for review December 12, 2025 05:38
@chunhtai
Copy link
Contributor

chunhtai commented Dec 12, 2025

Reading the API doc of ScrollMetricsNotification https://api.flutter.dev/flutter/widgets/ScrollMetricsNotification-class.html

this is useful for listening to [ScrollMetrics](https://api.flutter.dev/flutter/widgets/ScrollMetrics-mixin.html) changes that are not caused by the user scrolling.

kind of implying user scrolling shouldn't send a ScrollMetricsNotification

when the content of a scrollable is altered, making it larger or smaller, this notification will be dispatched. Similarly, if the size of the window or parent changes, the scrollable can notify of these changes in dimensions.

Implies ScrollMetricsNotification is related to content size changes.

It seems to me the CustomScrollView's behavior is incorrect unless the scrolling causes content size changes. Maybe what we want to do is that we should make sure it is not only scroll position change when the metrics changes before sending the notification

@yiiim
Copy link
Member Author

yiiim commented Dec 15, 2025

It seems to me the CustomScrollView's behavior is incorrect unless the scrolling causes content size changes. Maybe what we want to do is that we should make sure it is not only scroll position change when the metrics changes before sending the notification

Understood. I previously tried preventing CustomScrollView from dispatching ScrollMetricsNotification while scrolling, but that caused many issues and broke a lot of tests (e.g., #87076). I’ll likely need to address those failures incrementally before proceeding.

@yiiim yiiim marked this pull request as draft December 15, 2025 02:43
@Piinks
Copy link
Contributor

Piinks commented Dec 15, 2025

Hmm, I think maybe the behavior drifted and now correcting it one way or the other is proving difficult due to how breaking it is.

@yiiim you have done a lot of work here and in the other PRs related to this - which is super appreciated by the way! WDYT about writing down your findings from all of this in the doc template so you and @chunhtai and I can figure out the best course of action here?

That way we have all the info in one place and can discuss more easily what the right solution is. I think this will also be more respectful of your time 💙

@chunhtai
Copy link
Contributor

chunhtai commented Dec 15, 2025

Agree with @Piinks , discuss this in a doc seems better so that you don't have to go back and forth with the implementation until we figure out a way forward. Also appreciate all the work you have been put into this so far!

@yiiim
Copy link
Member Author

yiiim commented Dec 16, 2025

@yiiim you have done a lot of work here and in the other PRs related to this - which is super appreciated by the way! WDYT about writing down your findings from all of this in the doc template so you and @chunhtai and I can figure out the best course of action here?

Thank you so much for your comment! I think this is great. Due to language barriers, I do find the documentation a bit challenging, but I believe I will be able to complete it.

Agree with @Piinks , discuss this in a doc seems better so that you don't have to go back and forth with the implementation until we figure out a way forward. Also appreciate all the work you have been put into this so far!

That way we have all the info in one place and can discuss more easily what the right solution is. I think this will also be more respectful of your time 💙

I’ve always enjoyed the time and effort I’ve put into Flutter, and I really appreciate working together with all of you.

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

Labels

f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants