Skip to content

Commit 712db02

Browse files
authored
Fix the issue where calling showOnScreen on a sliver after a pinned child in SliverMainAxisGroup does not reveal it. (flutter#171339)
Fixes: flutter#154615 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- 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
1 parent 4b74066 commit 712db02

3 files changed

Lines changed: 57 additions & 4 deletions

File tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/// @docImport 'proxy_sliver.dart';
99
/// @docImport 'sliver_fill.dart';
1010
/// @docImport 'sliver_grid.dart';
11+
/// @docImport 'sliver_group.dart';
1112
/// @docImport 'sliver_list.dart';
1213
/// @docImport 'sliver_padding.dart';
1314
/// @docImport 'sliver_persistent_header.dart';
@@ -1665,6 +1666,10 @@ abstract class RenderSliver extends RenderObject {
16651666
///
16661667
/// The `child` must be a child of this sliver.
16671668
///
1669+
/// If there are pinned slivers before [child], the offset should be reduced
1670+
/// by the extent of the pinned children. This can occur in [RenderSliver]s
1671+
/// that have multiple sliver children, such as [RenderSliverMainAxisGroup].
1672+
///
16681673
/// This method differs from [childMainAxisPosition] in that
16691674
/// [childMainAxisPosition] gives the distance from the leading _visible_ edge
16701675
/// of the sliver whereas [childScrollOffset] gives the distance from sliver's

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,24 +223,48 @@ class RenderSliverMainAxisGroup extends RenderSliver
223223
@override
224224
double? childScrollOffset(RenderObject child) {
225225
assert(child.parent == this);
226+
assert(child is RenderSliver);
227+
final double extentOfPinnedSlivers = _maxScrollObstructionExtentBefore(child as RenderSliver);
226228
final GrowthDirection growthDirection = constraints.growthDirection;
227229
switch (growthDirection) {
228230
case GrowthDirection.forward:
229231
double childScrollOffset = 0.0;
230-
RenderSliver? current = childBefore(child as RenderSliver);
232+
RenderSliver? current = childBefore(child);
231233
while (current != null) {
232234
childScrollOffset += current.geometry!.scrollExtent;
233235
current = childBefore(current);
234236
}
235-
return childScrollOffset;
237+
return childScrollOffset - extentOfPinnedSlivers;
236238
case GrowthDirection.reverse:
237239
double childScrollOffset = 0.0;
238-
RenderSliver? current = childAfter(child as RenderSliver);
240+
RenderSliver? current = childAfter(child);
239241
while (current != null) {
240242
childScrollOffset -= current.geometry!.scrollExtent;
241243
current = childAfter(current);
242244
}
243-
return childScrollOffset;
245+
return childScrollOffset - extentOfPinnedSlivers;
246+
}
247+
}
248+
249+
double _maxScrollObstructionExtentBefore(RenderSliver child) {
250+
final GrowthDirection growthDirection = child.constraints.growthDirection;
251+
switch (growthDirection) {
252+
case GrowthDirection.forward:
253+
double pinnedExtent = 0.0;
254+
RenderSliver? current = firstChild;
255+
while (current != child) {
256+
pinnedExtent += current!.geometry!.maxScrollObstructionExtent;
257+
current = childAfter(current);
258+
}
259+
return pinnedExtent;
260+
case GrowthDirection.reverse:
261+
double pinnedExtent = 0.0;
262+
RenderSliver? current = lastChild;
263+
while (current != child) {
264+
pinnedExtent += current!.geometry!.maxScrollObstructionExtent;
265+
current = childBefore(current);
266+
}
267+
return pinnedExtent;
244268
}
245269
}
246270

packages/flutter/test/widgets/sliver_main_axis_group_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,30 @@ void main() {
11251125
expect(tester.getTopLeft(find.text('2')), const Offset(0, 101));
11261126
expect(tester.getTopLeft(find.text('-2')), const Offset(0, -49));
11271127
});
1128+
1129+
testWidgets('showOnScreen reveals the Sliver after a pinned child in SliverMainAxisGroup', (
1130+
WidgetTester tester,
1131+
) async {
1132+
final ScrollController controller = ScrollController();
1133+
addTearDown(controller.dispose);
1134+
await tester.pumpWidget(
1135+
_buildSliverMainAxisGroup(
1136+
viewportHeight: 100,
1137+
controller: controller,
1138+
slivers: <Widget>[
1139+
const PinnedHeaderSliver(child: SizedBox(height: 50)),
1140+
const SliverToBoxAdapter(child: SizedBox(height: 50, child: Text('1'))),
1141+
const SliverToBoxAdapter(child: SizedBox(height: 400)),
1142+
],
1143+
),
1144+
);
1145+
controller.jumpTo(200);
1146+
await tester.pumpAndSettle();
1147+
final RenderObject renderObject = tester.renderObject(find.text('1', skipOffstage: false));
1148+
renderObject.showOnScreen();
1149+
await tester.pumpAndSettle();
1150+
expect(tester.getTopLeft(find.text('1')), const Offset(0.0, 50.0));
1151+
});
11281152
}
11291153

11301154
Widget _buildSliverList({

0 commit comments

Comments
 (0)