skip to content

In Flutter 3.44 and later, what does a ListView's scrollCacheExtent control, and what did it replace?

level: middleimportance: nice to knowfreq 20%

answer

  1. area before and after the viewport
  2. 250 logical pixels by default
  3. pixels or viewport multiples
  4. replaced cacheExtent and cacheExtentStyle
  5. built and laid out early

basics

~20 s

scrollCacheExtent sets how far beyond each edge of the viewport a lazy list builds and lays out items before they are visible, 250 logical pixels by default. Since Flutter 3.44 it replaces the deprecated cacheExtent and cacheExtentStyle pair with one ScrollCacheExtent value.

solid answer

~50 s

A lazy list keeps a **cache area** before the leading edge and after the trailing edge of the viewport; items in it are built and laid out even though they are not on screen, so they are ready when the user scrolls. `scrollCacheExtent` sets its size with a `ScrollCacheExtent`: `ScrollCacheExtent.pixels(500)` for a pixel distance, or `ScrollCacheExtent.viewport(0.5)` for a multiple of the viewport's main-axis size. When unset, it is 250 logical pixels. Flutter 3.44 deprecated `cacheExtent`, a bare `double`, together with `cacheExtentStyle`, which said how to read it, in favour of this single typed value. Raising it can smooth fast flings through a heavy parts catalogue at the cost of more build and layout work and memory; lowering it saves work but shows items being built just in time. The cache area also enables implicit accessibility scrolling on iOS.

go deeper

for a junior

Know that lists prepare some items just off screen and that scrollCacheExtent sets how many.

for a middle

Explain the cache area on both sides, the 250-pixel default, the pixels and viewport forms, and the 3.44 replacement of cacheExtent.

for a senior

Tune the cache against memory and build cost from profiling, and know its role in iOS accessibility scrolling.

for a principal

Treat cache tuning as a last step after item cost is fixed, and track deprecations like cacheExtent in upgrade plans.

## What the cache area is A scrolling viewport does not stop at its visible edges. It tries to cover: 1. the **cache extent before** the leading edge, 2. the **visible** main-axis extent, and 3. the **cache extent after** the trailing edge. Items in the two cache regions are **built and laid out** even though no pixel of them is on screen yet. When the user scrolls, they are already there, so the frame does not have to build them just in time. Items outside all three regions are removed, unless they asked to be kept alive. ## The parameter | Value | Meaning | |---|---| | not set | `ScrollCacheExtent.pixels(250)` — `RenderAbstractViewport.defaultCacheExtent` | | `ScrollCacheExtent.pixels(n)` | `n` logical pixels on each side | | `ScrollCacheExtent.viewport(f)` | `f` times the viewport's main-axis size on each side | `ScrollCacheExtent` is a sealed class with those two factory constructors. A 600-pixel-tall list with `ScrollCacheExtent.viewport(2.0)` caches 1,200 pixels above and 1,200 below. ```dart ListView.builder( itemCount: parts.length, scrollCacheExtent: const ScrollCacheExtent.pixels(500), itemBuilder: (BuildContext context, int index) => PartRow(part: parts[index]), ) ``` ## What changed in 3.44 Before, scroll views had `cacheExtent`, a `double`, and viewports also had `cacheExtentStyle`, which said whether that double was pixels or a viewport fraction. The split made it easy to misread `cacheExtent: 0.5` as half a viewport when it meant half a pixel. Flutter 3.44 deprecated both in favour of `scrollCacheExtent`, which carries the value and its unit together. The migration is mechanical: - `cacheExtent: 500.0` becomes `scrollCacheExtent: const ScrollCacheExtent.pixels(500.0)`; - `cacheExtent: 0.5` with `CacheExtentStyle.viewport` becomes `const ScrollCacheExtent.viewport(0.5)`. The old names still compile with deprecation warnings. ## Tuning the trade-off **Raise it** when: - items are expensive to build, such as rows with part photos, and fast flings show blank or late items; - you want neighbouring items ready for accessibility navigation. **Lower it** when: - items are heavy in memory and the list is long; - each item starts work when built, such as a network request, and building ahead wastes it. Changing it does not make any single item cheaper: every item in the cache area costs the same build and layout as a visible one. If items are slow to build, fixing the item usually beats enlarging the cache. ## Accessibility The cache area also supports **implicit accessibility scrolling on iOS**: when accessibility focus moves from a visible item to one in the cache area, the framework scrolls it into view. With a cache extent of zero there are no off-screen neighbours for that implicit scroll to reach. ## Related knobs - `PageView` computes its cache extent from `allowImplicitScrolling`, and asserts that the two are consistent if you pass both. - Items kept alive by `AutomaticKeepAliveClientMixin` survive regardless of the cache area.

  • How do you migrate ListView(cacheExtent: 500.0) to current Flutter?
    Replace it with `scrollCacheExtent: const ScrollCacheExtent.pixels(500.0)`. A viewport-relative setting that used `cacheExtentStyle: CacheExtentStyle.viewport` becomes `ScrollCacheExtent.viewport(value)`.
  • Will a larger scrollCacheExtent fix a list whose rows are slow to build?
    It can hide the symptom during steady scrolling by building rows earlier, but every cached row still costs the same build and layout, and a fast fling can outrun any cache. Making rows cheaper to build addresses the cause.

saying these in an interview costs you the question

  • The cache area only extends after the trailing edge.
  • Items in the cache area are painted on screen.
  • The default cache extent is one full viewport.
  • cacheExtent was removed from Flutter in 3.44.
  • A bigger cache extent makes each item cheaper to build.