Skip to content

Commit 1f78d26

Browse files
gnpricegithub-actions[bot]
authored andcommitted
sticky_header: Handle scrollOffsetCorrection
Fixes #1309. Fixes #725. This is necessary when scrolling back to the origin over items that grew taller while off screen (and beyond the 250px of near-on-screen cached items). For example that can happen because a message was edited, or because new messages came in that were taller than those previously present.
1 parent 7d5dfd9 commit 1f78d26

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

lib/widgets/sticky_header.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,10 @@ class _RenderSliverStickyHeaderList extends RenderSliver with RenderSliverHelper
564564
child!.layout(constraints, parentUsesSize: true);
565565
SliverGeometry geometry = child!.geometry!;
566566

567-
// TODO(#1309) handle the scrollOffsetCorrection case, passing it through
567+
if (geometry.scrollOffsetCorrection != null) {
568+
this.geometry = geometry;
569+
return;
570+
}
568571

569572
// We assume [child]'s geometry is free of certain complications.
570573
// Probably most or all of these *could* be handled if necessary, just at

test/widgets/sticky_header_test.dart

+50
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,56 @@ void main() {
103103
}
104104
}
105105
}
106+
107+
testWidgets('sticky headers: propagate scrollOffsetCorrection properly', (tester) async {
108+
Widget page(Widget Function(BuildContext, int) itemBuilder) {
109+
return Directionality(textDirection: TextDirection.ltr,
110+
child: StickyHeaderListView.builder(
111+
cacheExtent: 0,
112+
itemCount: 10, itemBuilder: itemBuilder));
113+
}
114+
115+
await tester.pumpWidget(page((context, i) =>
116+
StickyHeaderItem(
117+
allowOverflow: true,
118+
header: _Header(i, height: 40),
119+
child: _Item(i, height: 200))));
120+
check(tester.getTopLeft(find.text("Item 2"))).equals(Offset(0, 400));
121+
122+
// Scroll down (dragging up) to get item 0 off screen.
123+
await tester.drag(find.text("Item 2"), Offset(0, -300));
124+
await tester.pump();
125+
check(tester.getTopLeft(find.text("Item 2"))).equals(Offset(0, 100));
126+
127+
// Make the off-screen item 0 taller, so scrolling back up will underflow.
128+
await tester.pumpWidget(page((context, i) =>
129+
StickyHeaderItem(
130+
allowOverflow: true,
131+
header: _Header(i, height: 40),
132+
child: _Item(i, height: i == 0 ? 400 : 200))));
133+
// Confirm the change in item 0's height hasn't already been applied,
134+
// as it would if the item were within the viewport or its cache area.
135+
check(tester.getTopLeft(find.text("Item 2"))).equals(Offset(0, 100));
136+
137+
// Scroll back up (dragging down). This will cause a correction as the list
138+
// discovers that moving 300px up doesn't reach the start anymore.
139+
await tester.drag(find.text("Item 2"), Offset(0, 300));
140+
141+
// As a bonus, mark one of the already-visible items as needing layout.
142+
// (In a real app, this would typically happen because some state changed.)
143+
tester.firstElement(find.widgetWithText(SizedBox, "Item 2"))
144+
.renderObject!.markNeedsLayout();
145+
146+
// If scrollOffsetCorrection doesn't get propagated to the viewport, this
147+
// pump will record an exception (causing the test to fail at the end)
148+
// because the marked item won't get laid out.
149+
await tester.pump();
150+
check(tester.getTopLeft(find.text("Item 2"))).equals(Offset(0, 400));
151+
152+
// Moreover if scrollOffsetCorrection doesn't get propagated, this item
153+
// will get placed at zero rather than properly extend up off screen.
154+
check(tester.getTopLeft(find.text("Item 0"))).equals(Offset(0, -200));
155+
});
106156
}
107157

108158
Future<void> _checkSequence(

0 commit comments

Comments
 (0)