Fix issue where SliverPersistentHeader that is both floating and pinned would scroll down when scrolling past the beginning of the ScrollView (#27433)
diff --git a/packages/flutter/lib/src/rendering/sliver_persistent_header.dart b/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
index a00a684..84a9b0c 100644
--- a/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
+++ b/packages/flutter/lib/src/rendering/sliver_persistent_header.dart
@@ -524,6 +524,7 @@
final double layoutExtent = maxExtent - constraints.scrollOffset;
geometry = SliverGeometry(
scrollExtent: maxExtent,
+ paintOrigin: math.min(constraints.overlap, 0.0),
paintExtent: clampedPaintExtent,
layoutExtent: layoutExtent.clamp(0.0, clampedPaintExtent),
maxPaintExtent: maxExtent,
diff --git a/packages/flutter/test/widgets/slivers_appbar_floating_pinned_test.dart b/packages/flutter/test/widgets/slivers_appbar_floating_pinned_test.dart
index 69614fe..563920b 100644
--- a/packages/flutter/test/widgets/slivers_appbar_floating_pinned_test.dart
+++ b/packages/flutter/test/widgets/slivers_appbar_floating_pinned_test.dart
@@ -112,4 +112,106 @@
expect(render.geometry.paintExtent, availableHeight);
expect(render.geometry.layoutExtent, 0.0);
});
+
+ testWidgets('Pinned and floating SliverAppBar sticks to top the content is scroll down', (WidgetTester tester) async {
+ const Key anchor = Key('drag');
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Center(
+ child: Container(
+ height: 300,
+ color: Colors.green,
+ child: CustomScrollView(
+ physics: const BouncingScrollPhysics(),
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ floating: true,
+ expandedHeight: 100.0,
+ ),
+ SliverToBoxAdapter(child: Container(key: anchor, color: Colors.red, height: 100)),
+ SliverToBoxAdapter(child: Container(height: 600, color: Colors.green)),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ final RenderSliverFloatingPinnedPersistentHeader render = tester.renderObject(find.byType(SliverAppBar));
+
+ const double scrollDistance = 40;
+ final TestGesture gesture = await tester.press(find.byKey(anchor));
+ await gesture.moveBy(const Offset(0, scrollDistance));
+ await tester.pump();
+
+ expect(render.geometry.paintOrigin, -scrollDistance);
+ });
+
+ testWidgets('Floating SliverAppBar sticks to top the content is scroll down', (WidgetTester tester) async {
+ const Key anchor = Key('drag');
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Center(
+ child: Container(
+ height: 300,
+ color: Colors.green,
+ child: CustomScrollView(
+ physics: const BouncingScrollPhysics(),
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: false,
+ floating: true,
+ expandedHeight: 100.0,
+ ),
+ SliverToBoxAdapter(child: Container(key: anchor, color: Colors.red, height: 100)),
+ SliverToBoxAdapter(child: Container(height: 600, color: Colors.green)),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ final RenderSliverFloatingPersistentHeader render = tester.renderObject(find.byType(SliverAppBar));
+
+ const double scrollDistance = 40;
+ final TestGesture gesture = await tester.press(find.byKey(anchor));
+ await gesture.moveBy(const Offset(0, scrollDistance));
+ await tester.pump();
+
+ expect(render.geometry.paintOrigin, -scrollDistance);
+ });
+
+ testWidgets('Pinned SliverAppBar sticks to top the content is scroll down', (WidgetTester tester) async {
+ const Key anchor = Key('drag');
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Center(
+ child: Container(
+ height: 300,
+ color: Colors.green,
+ child: CustomScrollView(
+ physics: const BouncingScrollPhysics(),
+ slivers: <Widget>[
+ const SliverAppBar(
+ pinned: true,
+ floating: false,
+ expandedHeight: 100.0,
+ ),
+ SliverToBoxAdapter(child: Container(key: anchor, color: Colors.red, height: 100)),
+ SliverToBoxAdapter(child: Container(height: 600, color: Colors.green)),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ final RenderSliverPinnedPersistentHeader render = tester.renderObject(find.byType(SliverAppBar));
+
+ const double scrollDistance = 40;
+ final TestGesture gesture = await tester.press(find.byKey(anchor));
+ await gesture.moveBy(const Offset(0, scrollDistance));
+ await tester.pump();
+
+ expect(render.geometry.paintOrigin, -scrollDistance);
+ });
}