Skip to content

Commit 825e901

Browse files
authored
Set cacheExtent for SliverFillRemaining widget (#143612)
When a Sliver with items is outside of the Viewport, but within the Viewport's `cacheExtent`, the framework should create SemanticNodes for the items even though they are out of view. However, for this to work, the Sliver's geometry must have a `cacheExtent` (how much space the sliver took up of the Viewport's `cacheExtent`) greater than 0, otherwise it is [excluded](https://github.com/flutter/flutter/blob/f01ce9f4cb41beff7b85122b5fcf1228bb655a87/packages/flutter/lib/src/rendering/viewport.dart#L311-L315). `SliverFillRemaining` widgets that fall outside the viewport did not have this set and therefore were being excluded when SemanticNodes were created, even if they were within the Viewport's `cacheExtent`. This PR sets the `cacheExtent` for `SliverFillRemaining` widgets. In addition, `RenderSliverFillRemainingWithScrollable` would get dropped from the semantic tree because it's child had a size of 0 when outside the remaining paint extent. To fix, we give the child a `maxExtent` of the sliver's `cacheExtent` if it's outside the remaining paint extent but within the viewport's cacheExtent. Fixes flutter/flutter#142065. Definitions: * `RenderViewport.cacheExtent`: ```dart /// The viewport has an area before and after the visible area to cache items /// that are about to become visible when the user scrolls. /// /// Items that fall in this cache area are laid out even though they are not /// (yet) visible on screen. The [cacheExtent] describes how many pixels /// the cache area extends before the leading edge and after the trailing edge /// of the viewport. /// /// The total extent, which the viewport will try to cover with children, is /// [cacheExtent] before the leading edge + extent of the main axis + /// [cacheExtent] after the trailing edge. /// /// The cache area is also used to implement implicit accessibility scrolling /// on iOS: When the accessibility focus moves from an item in the visible /// viewport to an invisible item in the cache area, the framework will bring /// that item into view with an (implicit) scroll action. ``` * `SliverGeometry.cacheExtent`: ```dart /// How many pixels the sliver has consumed in the /// [SliverConstraints.remainingCacheExtent]. ``` * `SliverContraints.remainingCacheExtent`: ```dart /// Describes how much content the sliver should provide starting from the /// [cacheOrigin]. /// /// Not all content in the [remainingCacheExtent] will be visible as some /// of it might fall into the cache area of the viewport. /// /// Each sliver should start laying out content at the [cacheOrigin] and /// try to provide as much content as the [remainingCacheExtent] allows. ```
1 parent 1ccad1a commit 825e901

File tree

3 files changed

+1329
-1
lines changed

3 files changed

+1329
-1
lines changed

packages/flutter/lib/src/rendering/sliver_fill.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,21 +83,36 @@ class RenderSliverFillRemainingWithScrollable extends RenderSliverSingleBoxAdapt
8383
final SliverConstraints constraints = this.constraints;
8484
final double extent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);
8585

86+
final double cacheExtent = calculateCacheOffset(
87+
constraints,
88+
from: 0.0,
89+
to: constraints.viewportMainAxisExtent,
90+
);
8691
if (child != null) {
92+
double maxExtent = extent;
93+
94+
// If sliver has no extent, but is within viewport's cacheExtent, use the
95+
// sliver's cacheExtent as the maxExtent so that it does not get dropped
96+
// from the semantic tree.
97+
if (extent == 0 && cacheExtent > 0) {
98+
maxExtent = cacheExtent;
99+
}
87100
child!.layout(constraints.asBoxConstraints(
88101
minExtent: extent,
89-
maxExtent: extent,
102+
maxExtent: maxExtent,
90103
));
91104
}
92105

93106
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
94107
assert(paintedChildSize.isFinite);
95108
assert(paintedChildSize >= 0.0);
109+
96110
geometry = SliverGeometry(
97111
scrollExtent: constraints.viewportMainAxisExtent,
98112
paintExtent: paintedChildSize,
99113
maxPaintExtent: paintedChildSize,
100114
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
115+
cacheExtent: cacheExtent,
101116
);
102117
if (child != null) {
103118
setChildParentData(child!, constraints, geometry!);
@@ -162,11 +177,14 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter {
162177
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
163178
assert(paintedChildSize.isFinite);
164179
assert(paintedChildSize >= 0.0);
180+
181+
final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: extent);
165182
geometry = SliverGeometry(
166183
scrollExtent: extent,
167184
paintExtent: paintedChildSize,
168185
maxPaintExtent: paintedChildSize,
169186
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
187+
cacheExtent: cacheExtent,
170188
);
171189
if (child != null) {
172190
setChildParentData(child!, constraints, geometry!);
@@ -235,11 +253,14 @@ class RenderSliverFillRemainingAndOverscroll extends RenderSliverSingleBoxAdapte
235253
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
236254
assert(paintedChildSize.isFinite);
237255
assert(paintedChildSize >= 0.0);
256+
257+
final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: extent);
238258
geometry = SliverGeometry(
239259
scrollExtent: extent,
240260
paintExtent: math.min(maxExtent, constraints.remainingPaintExtent),
241261
maxPaintExtent: maxExtent,
242262
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
263+
cacheExtent: cacheExtent,
243264
);
244265
if (child != null) {
245266
setChildParentData(child!, constraints, geometry!);

0 commit comments

Comments
 (0)