Skip to content

Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex…#181092

Merged
auto-submit[bot] merged 16 commits intoflutter:masterfrom
chunhtai:wires-cache-type
Feb 10, 2026
Merged

Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex…#181092
auto-submit[bot] merged 16 commits intoflutter:masterfrom
chunhtai:wires-cache-type

Conversation

@chunhtai
Copy link
Contributor

@chunhtai chunhtai commented Jan 16, 2026

…tent NaN problem

besides the general piping through parameters and breaking change. Fixing an issue when the shrinkwrap: true the mainAxisExetent will be double.infinity, which cause the cacheExtent to be double.Nan after the calculation and crash the app.

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.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: scrolling Viewports, list views, slivers, etc. labels Jan 16, 2026
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 introduces a cacheExtentStyle property to ScrollView and related widgets, allowing the cacheExtent to be interpreted as pixels or as a multiplier of the viewport size. The changes are correctly wired through the widget and render layers. Additionally, it fixes an issue where an unbounded RenderShrinkWrappingViewport could cause a NaN value for the cache extent. The changes are well-tested with a new test file that covers various scroll views and the bug fix. My review includes one minor suggestion to improve the consistency of debug output.

@chunhtai chunhtai requested a review from Piinks January 20, 2026 18:24
@chunhtai
Copy link
Contributor Author

actually I should also refactor the use of a single parameter instead of two. convert to draft

@chunhtai chunhtai marked this pull request as draft January 20, 2026 19:00
@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.

@github-actions github-actions bot added the f: material design flutter/packages/flutter/material repository. label Jan 20, 2026
@chunhtai chunhtai changed the title Wires up cache extent type and also fixes unbound shrinkwrap cache ex… Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex… Jan 21, 2026
@chunhtai
Copy link
Contributor Author

@gemini-code-assist code review

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 introduces ScrollCacheExtent to provide a more robust API for defining cache extents, replacing the separate cacheExtent and cacheExtentStyle properties. This is a significant and well-executed refactoring that improves API clarity. The changes are applied consistently across various scrolling widgets. Additionally, the PR fixes a bug related to shrinkWrap causing a NaN value for the cache extent.

I've identified a critical issue in one of the new deprecated setters that would cause a compilation error, along with a couple of minor issues regarding efficiency and documentation. My review includes suggestions to address these points.

@chunhtai chunhtai marked this pull request as ready for review January 21, 2026 20:30
@chunhtai
Copy link
Contributor Author

Hi @Piinks , this is ready for review

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 introduces ScrollCacheExtent, a new class to define the cache area for scrollable content, replacing the separate cacheExtent and cacheExtentStyle properties. The change refactors numerous widgets, including ScrollView, ListView, GridView, PageView, and Viewport, to adopt this new API. The old properties are deprecated but handled for backward compatibility. Additionally, the PR fixes an issue in RenderShrinkWrappingViewport where an infinite mainAxisExtent could cause a NaN value for the cache extent. New tests are added to verify the functionality of scrollCacheExtent and the bug fix. My feedback includes a couple of minor suggestions for code cleanup.

@chunhtai
Copy link
Contributor Author

google test doesn't seem related

///
/// * [Viewport.scrollCacheExtent], which uses this class to define the cache area.
@immutable
sealed class ScrollCacheExtent {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is so nice. Chef's kiss.

Copy link
Contributor

Choose a reason for hiding this comment

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

WDYT about a ScrollCacheExtent that is for lazy loaders like RenderSliverList that allows the user to specify the number of children to lay out beyond the viewport bounds that are not visible?
Maybe something for later, not blocking.

Copy link
Contributor

Choose a reason for hiding this comment

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

Meh, this might be harder. Here currently, both methods boil down to a pixel value.
Maybe for the other child-based idea, we should consider it a feature on lazy loader to ensure there is always one fully out of bounds child available to progress scroll forward or backward to. Just musing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

by child count will be easier to implement with the new class. but the main problem with child count is that whether it is renderobject child count or semantics child count. By the use case of cache extent, it makes more sense to be by semantics child count. Such information is only available after semantics tree build, which is at the very end of the draw frame. That means we may need multiple run of layout to properly implement cache by semantics child count

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

This is awesome. Thank you. 🙏

const ScrollCacheExtent._();

/// Creates a cache extent in logical pixels.
const factory ScrollCacheExtent.pixels(double pixels) = _PixelScrollCacheExtent;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a dart fix that transforms a previously set cacheExtent to this with the value provided as pixels?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this will depends on cacheExtenType, is it possible to conditionally use different constructor based on whether the other parameter is set or not?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, in the fix, you should be able to check cacheExtent, cachExtentStyle, and then add the new parameter and provide the right constructor based on the values. Them you can use remove parameter to remove the old ones.

Copy link
Contributor

@Piinks Piinks Feb 6, 2026

Choose a reason for hiding this comment

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

Why don't we do the dart fix in a separate change? There are many classes to account for, and it might be a bit complicated.

///
/// * [Viewport.scrollCacheExtent], which uses this class to define the cache area.
@immutable
sealed class ScrollCacheExtent {
Copy link
Contributor

Choose a reason for hiding this comment

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

WDYT about a ScrollCacheExtent that is for lazy loaders like RenderSliverList that allows the user to specify the number of children to lay out beyond the viewport bounds that are not visible?
Maybe something for later, not blocking.

///
/// * [Viewport.scrollCacheExtent], which uses this class to define the cache area.
@immutable
sealed class ScrollCacheExtent {
Copy link
Contributor

Choose a reason for hiding this comment

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

Meh, this might be harder. Here currently, both methods boil down to a pixel value.
Maybe for the other child-based idea, we should consider it a feature on lazy loader to ensure there is always one fully out of bounds child available to progress scroll forward or backward to. Just musing.

markNeedsLayout();
}

// TODO(ianh): cacheExtent/cacheExtentStyle should be a single
Copy link
Contributor

Choose a reason for hiding this comment

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

Hooray for resolving old TODOs!

double _attemptLayout(double mainAxisExtent, double crossAxisExtent, double correctedOffset) {
assert(!mainAxisExtent.isNaN);
assert(mainAxisExtent >= 0.0);
assert(mainAxisExtent.isFinite);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this safe to add? Does the infinite scroll view case take a different path?

Copy link
Contributor

Choose a reason for hiding this comment

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

How does this differ from the shrink wrapping viewport below?

Copy link
Contributor Author

@chunhtai chunhtai Feb 5, 2026

Choose a reason for hiding this comment

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

can a viewport to be in a unbounded container? I thought that will throw an layout error. but i am not too sure

How does this differ from the shrink wrapping viewport below?

I think only shrink wrap viewport is allowed to be layout in unbound situation

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yeah I see now. Normal viewport checks debugCheckHasBoundedAxis during computeDryLayout. Do you want to call that method instead of the assertion?
Shrinkwrapping does in fact allow infinite main axis extent. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

debugCheckHasBoundedAxis checks against constraints which this method has already teared down into axis extent. I will keep this assert as is. let me know otherwise

Copy link
Contributor

Choose a reason for hiding this comment

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

Right right! Gotcha. This assert should be ok then. 👍

@chunhtai chunhtai requested a review from Piinks February 6, 2026 20:05
Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

This LGTM. We can do the dart fix in a separate change 👍
I'll file an issue to follow up on the 2D scrolling package to migrate those when this gets to stable.

@chunhtai
Copy link
Contributor Author

chunhtai commented Feb 7, 2026

I will include dart fix since gemini finds it easy to add

@github-actions github-actions bot added the c: tech-debt Technical debt, code quality, testing, etc. label Feb 9, 2026
sfshaza2 added a commit to flutter/website that referenced this pull request Feb 9, 2026
migration guide for flutter/flutter#181092

## Presubmit checklist

- [ ] If you are unwilling, or unable, to sign the CLA, even for a
_tiny_, one-word PR, please file an issue instead of a PR.
- [ ] If this PR is not meant to land until a future stable release,
mark it as draft with an explanation.
- [ ] This PR follows the [Google Developer Documentation Style
Guidelines](https://developers.google.com/style)—for example, it doesn't
use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first-person
pronouns).
- [ ] This PR uses [semantic line
breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks)
  of 80 characters or fewer.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Shams Zakhour <[email protected]>
@chunhtai chunhtai requested a review from Piinks February 9, 2026 20:41
@Piinks
Copy link
Contributor

Piinks commented Feb 9, 2026

I will include dart fix since gemini finds it easy to add

Oh my gosh. That's amazing

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

This LGTM!
Do you think we need a migration guide on the website?

@chunhtai
Copy link
Contributor Author

chunhtai commented Feb 9, 2026

@chunhtai chunhtai added the autosubmit Merge PR when tree becomes green via auto submit App label Feb 9, 2026
@auto-submit auto-submit bot added this pull request to the merge queue Feb 9, 2026
Merged via the queue into flutter:master with commit 2391bdb Feb 10, 2026
72 of 73 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Feb 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 10, 2026
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Feb 10, 2026
Roll Flutter from e8f9dc50356d to 9bda20a11f1e (34 revisions)

flutter/flutter@e8f9dc5...9bda20a

2026-02-10 [email protected] Remove Material import from focus_traversal_test.dart (flutter/flutter#180994)
2026-02-10 [email protected] Roll Skia from 6e217430c052 to cffb3bf918df (1 revision) (flutter/flutter#182131)
2026-02-10 [email protected] Encourage splitting large test files in testing documentation 2 (flutter/flutter#182051)
2026-02-10 [email protected] refactor: migrate CupertinoPageTransitionsBuilder to cupertino folder (flutter/flutter#179776)
2026-02-10 [email protected] Delete the last remaining skia only fragment shader tests (flutter/flutter#182127)
2026-02-10 [email protected] [a11y][android] Set new CheckState APIs for android API 36  (flutter/flutter#182113)
2026-02-10 [email protected] Add missing dependencies to framework_tests_misc_leak_tracking (flutter/flutter#181929)
2026-02-10 [email protected] Roll Dart SDK from eee0e2e11174 to 69eb951f8f7e (2 revisions) (flutter/flutter#182128)
2026-02-10 [email protected] Marks Linux_android_emu android_display_cutout to be flaky (flutter/flutter#181901)
2026-02-10 [email protected] Bump Dart to 3.10 (flutter/flutter#174066)
2026-02-10 [email protected] Roll Skia from d4b7e24a209b to 6e217430c052 (6 revisions) (flutter/flutter#182126)
2026-02-09 [email protected] Intercept UIScene device log and print a guided warning (flutter/flutter#181515)
2026-02-09 [email protected] Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex… (flutter/flutter#181092)
2026-02-09 [email protected] [Android] Add mechanism for setting Android engine flags via Android manifest (take 2) (flutter/flutter#181632)
2026-02-09 [email protected] Fix wrong comment about default impeller value (flutter/flutter#181831)
2026-02-09 [email protected] fix build fail for wayland only platform (flutter/flutter#182057)
2026-02-09 [email protected] [AGP 9] Added Warning Against Updating to AGP 9 (flutter/flutter#181977)
2026-02-09 [email protected] Updated Shaderc dep (flutter/flutter#180976)
2026-02-09 [email protected] Refactor accessibility guidelines out to widget layer (flutter/flutter#181672)
2026-02-09 [email protected] Roll Skia from 68dff53238e5 to d4b7e24a209b (2 revisions) (flutter/flutter#182087)
2026-02-09 [email protected] fix: OutlineInputBorder not respecting BorderSide stroke alignment (flutter/flutter#180487)
2026-02-09 [email protected] Adds opengles to engine dart tests (flutter/flutter#181933)
2026-02-09 [email protected] Add command to build a Swift Package for Add to App and generate FlutterPluginRegistrant (flutter/flutter#181224)
2026-02-09 [email protected] Remove unused constant in `bundle.dart` (flutter/flutter#182023)
2026-02-09 [email protected] Roll Fuchsia Linux SDK from iqtwdXlgKIyZkL5Li... to 7BGf7mPQvgLi7Axb6... (flutter/flutter#182082)
2026-02-09 [email protected] Remove unused getters in `user_messages.dart` (flutter/flutter#181867)
2026-02-09 [email protected] Roll Packages from 7805d3e to 3d5eaa5 (3 revisions) (flutter/flutter#182083)
2026-02-09 [email protected] Roll Skia from 5d891cd7fb7f to 68dff53238e5 (1 revision) (flutter/flutter#182080)
2026-02-09 [email protected] Update example description (flutter/flutter#182067)
2026-02-09 [email protected] Roll Dart SDK from 965b51c219d3 to eee0e2e11174 (1 revision) (flutter/flutter#182073)
2026-02-09 [email protected] Roll Skia from 9533d7533d59 to 5d891cd7fb7f (6 revisions) (flutter/flutter#182070)
2026-02-09 [email protected] Add a new flutter cli command, running-apps, using mDNS app discovery (flutter/flutter#180098)
2026-02-09 [email protected] Roll Skia from b7db9f35f0f2 to 9533d7533d59 (2 revisions) (flutter/flutter#182069)
2026-02-08 [email protected] Improve FlWindowMonitor API (flutter/flutter#181885)

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

To file a bug in Packages: 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

...
flutter-zl pushed a commit to flutter-zl/flutter that referenced this pull request Feb 10, 2026
…x… (flutter#181092)

…tent NaN problem

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

besides the general piping through parameters and breaking change.
Fixing an issue when the shrinkwrap: true the mainAxisExetent will be
double.infinity, which cause the cacheExtent to be double.Nan after the
calculation and crash the app.

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@Piinks Piinks mentioned this pull request Feb 12, 2026
9 tasks
rickhohler pushed a commit to rickhohler/flutter that referenced this pull request Feb 19, 2026
…x… (flutter#181092)

…tent NaN problem

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

besides the general piping through parameters and breaking change.
Fixing an issue when the shrinkwrap: true the mainAxisExetent will be
double.infinity, which cause the cacheExtent to be double.Nan after the
calculation and crash the app.

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: tech-debt Technical debt, code quality, testing, etc. f: material design flutter/packages/flutter/material repository. 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.

2 participants