Reland: Skip painting hidden children of a sliver list (#18539)

* Revert "manual rollback of of 18530 (#18531)"

This reverts commit 3f79f8cba5425e964f05fc79331dc5ff4b82c22f.

* Fix the offset calculation

* Add a unit test
diff --git a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
index 2273acc..1323440 100644
--- a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
+++ b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
@@ -524,7 +524,13 @@
       );
       if (addExtent)
         childOffset += mainAxisUnit * paintExtentOf(child);
-      context.paintChild(child, childOffset);
+
+      // If the child's visible interval (mainAxisLowerBound, mainAxisLowerBound + paintExtentOf(child))
+      // does not intersect the paint extent interval (0, constraints.remainingPaintExtent), it's hidden.
+      final double mainAxisLowerBound = mainAxisDelta + (addExtent ? paintExtentOf(child) : 0);
+      if (mainAxisLowerBound < constraints.remainingPaintExtent && mainAxisLowerBound + paintExtentOf(child) > 0)
+        context.paintChild(child, childOffset);
+
       child = childAfter(child);
     }
   }
diff --git a/packages/flutter/test/widgets/keep_alive_test.dart b/packages/flutter/test/widgets/keep_alive_test.dart
index 566fa0e..05e3baf 100644
--- a/packages/flutter/test/widgets/keep_alive_test.dart
+++ b/packages/flutter/test/widgets/keep_alive_test.dart
@@ -307,14 +307,14 @@
       '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     │     size: Size(800.0, 400.0)\n'
       '                     │\n'
-      '                     └─child with index 2: RenderLimitedBox#00000\n'
+      '                     └─child with index 2: RenderLimitedBox#00000 NEEDS-PAINT\n'
       '                       │ parentData: index=2; layoutOffset=800.0\n'
       '                       │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                       │ size: Size(800.0, 400.0)\n'
       '                       │ maxWidth: 400.0\n'
       '                       │ maxHeight: 400.0\n'
       '                       │\n'
-      '                       └─child: RenderCustomPaint#00000\n'
+      '                       └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
       '                           parentData: <none> (can use size)\n'
       '                           constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                           size: Size(800.0, 400.0)\n'
@@ -412,14 +412,14 @@
       '                     │   cacheExtent: 1100.0)\n'
       '                     │ currently live children: 4 to 7\n'
       '                     │\n'
-      '                     ├─child with index 4: RenderLimitedBox#00000\n'
+      '                     ├─child with index 4: RenderLimitedBox#00000 NEEDS-PAINT\n'
       '                     │ │ parentData: index=4; layoutOffset=1600.0\n'
       '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     │ │ size: Size(800.0, 400.0)\n'
       '                     │ │ maxWidth: 400.0\n'
       '                     │ │ maxHeight: 400.0\n'
       '                     │ │\n'
-      '                     │ └─child: RenderCustomPaint#00000\n'
+      '                     │ └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
       '                     │     parentData: <none> (can use size)\n'
       '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     │     size: Size(800.0, 400.0)\n'
@@ -448,14 +448,14 @@
       '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     │     size: Size(800.0, 400.0)\n'
       '                     │\n'
-      '                     ├─child with index 7: RenderLimitedBox#00000\n'
+      '                     ├─child with index 7: RenderLimitedBox#00000 NEEDS-PAINT\n'
       '                     ╎ │ parentData: index=7; layoutOffset=2800.0\n'
       '                     ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     ╎ │ size: Size(800.0, 400.0)\n'
       '                     ╎ │ maxWidth: 400.0\n'
       '                     ╎ │ maxHeight: 400.0\n'
       '                     ╎ │\n'
-      '                     ╎ └─child: RenderCustomPaint#00000\n'
+      '                     ╎ └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
       '                     ╎     parentData: <none> (can use size)\n'
       '                     ╎     constraints: BoxConstraints(w=800.0, h=400.0)\n'
       '                     ╎     size: Size(800.0, 400.0)\n'
diff --git a/packages/flutter/test/widgets/list_view_viewporting_test.dart b/packages/flutter/test/widgets/list_view_viewporting_test.dart
index c4b8632..9e4fb45 100644
--- a/packages/flutter/test/widgets/list_view_viewporting_test.dart
+++ b/packages/flutter/test/widgets/list_view_viewporting_test.dart
@@ -459,5 +459,36 @@
 
     final RenderSliverList list = tester.renderObject(find.byType(SliverList));
     expect(list, paintsExactlyCountTimes(#drawParagraph, 2));
-  }, skip: true);
+  });
+
+  testWidgets('ListView should paint with offset', (WidgetTester tester) async {
+    await tester.pumpWidget(
+        new MaterialApp(
+            home: new Scaffold(
+                body: new Container(
+                    height: 500.0,
+                    child: new CustomScrollView(
+                      controller: new ScrollController(initialScrollOffset: 120.0),
+                      slivers: <Widget>[
+                        const SliverAppBar(
+                          expandedHeight: 250.0,
+                        ),
+                        new SliverList(
+                            delegate: new ListView.builder(
+                                itemExtent: 100.0,
+                                itemCount: 100,
+                                itemBuilder: (_, __) => new Container(
+                                  height: 40.0,
+                                  child: const Text('hey'),
+                                )).childrenDelegate),
+                      ],
+                    )
+                )
+            )
+        )
+    );
+
+    final RenderObject renderObject = tester.renderObject(find.byType(Scrollable));
+    expect(renderObject, paintsExactlyCountTimes(#drawParagraph, 10));
+  });
 }
diff --git a/packages/flutter/test/widgets/sliver_fill_viewport_test.dart b/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
index 3ba4b77..d6177f6 100644
--- a/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
+++ b/packages/flutter/test/widgets/sliver_fill_viewport_test.dart
@@ -82,7 +82,7 @@
         ' │ │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
         ' │ │ layer: OffsetLayer#00000\n'
         ' │ │ size: Size(800.0, 600.0)\n'
-        ' │ │ metrics: 75.0% useful (1 bad vs 3 good)\n'
+        ' │ │ metrics: 66.7% useful (1 bad vs 2 good)\n'
         ' │ │ diagnosis: insufficient data to draw conclusion (less than five\n'
         ' │ │   repaints)\n'
         ' │ │\n'
@@ -103,9 +103,9 @@
         ' └─child with index 1: RenderRepaintBoundary#00000\n'
         '   │ parentData: index=1; layoutOffset=600.0\n'
         '   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
-        '   │ layer: OffsetLayer#00000\n'
+        '   │ layer: OffsetLayer#00000 DETACHED\n'
         '   │ size: Size(800.0, 600.0)\n'
-        '   │ metrics: 75.0% useful (1 bad vs 3 good)\n'
+        '   │ metrics: 50.0% useful (1 bad vs 1 good)\n'
         '   │ diagnosis: insufficient data to draw conclusion (less than five\n'
         '   │   repaints)\n'
         '   │\n'