Separate keep alive logic from SliverMultiBox classes (#24192)

* sliver separation and test
diff --git a/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..949b678
--- /dev/null
+++ b/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>BuildSystemType</key>
+	<string>Original</string>
+</dict>
+</plist>
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 75e7741..3757425 100644
--- a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
+++ b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
@@ -121,17 +121,35 @@
   /// true without making any assertions.
   bool debugAssertChildListLocked() => true;
 }
-
-/// Parent data structure used by [RenderSliverMultiBoxAdaptor].
-class SliverMultiBoxAdaptorParentData extends SliverLogicalParentData with ContainerParentDataMixin<RenderBox> {
-  /// The index of this child according to the [RenderSliverBoxChildManager].
-  int index;
-
+/// Parent data structure used by [RenderSliverWithKeepAliveMixin].
+mixin KeepAliveParentDataMixin implements ParentData {
   /// Whether to keep the child alive even when it is no longer visible.
   bool keepAlive = false;
 
   /// Whether the widget is currently being kept alive, i.e. has [keepAlive] set
   /// to true and is offscreen.
+  bool get keptAlive;
+}
+
+/// This class exists to dissociate [KeepAlive] from [RenderSliverMultiBoxAdaptor].
+///
+/// [RenderSliverWithKeepAliveMixin.setupParentData] must be implemented to use
+/// a parentData class that uses the right mixin or whatever is appropriate.
+mixin RenderSliverWithKeepAliveMixin implements RenderSliver {
+  /// Alerts the developer that the child's parentData needs to be of type
+  /// [KeepAliveParentDataMixin].
+  @override
+  void setupParentData(RenderObject child) {
+    assert(child.parentData is KeepAliveParentDataMixin);
+  }
+}
+
+/// Parent data structure used by [RenderSliverMultiBoxAdaptor].
+class SliverMultiBoxAdaptorParentData extends SliverLogicalParentData with ContainerParentDataMixin<RenderBox>, KeepAliveParentDataMixin {
+  /// The index of this child according to the [RenderSliverBoxChildManager].
+  int index;
+
+  @override
   bool get keptAlive => _keptAlive;
   bool _keptAlive = false;
 
@@ -166,7 +184,7 @@
 ///  * [RenderSliverGrid], which places its children in arbitrary positions.
 abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
   with ContainerRenderObjectMixin<RenderBox, SliverMultiBoxAdaptorParentData>,
-       RenderSliverHelpers {
+       RenderSliverHelpers, RenderSliverWithKeepAliveMixin {
 
   /// Creates a sliver with multiple box children.
   ///
@@ -585,4 +603,4 @@
     }
     return children;
   }
-}
+}
\ No newline at end of file
diff --git a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
index 170196b..b03cc7b 100644
--- a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
+++ b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart
@@ -80,7 +80,7 @@
     handle.addListener(_handles[handle]);
     if (!_keepingAlive) {
       _keepingAlive = true;
-      final ParentDataElement<SliverMultiBoxAdaptorWidget> childElement = _getChildElement();
+      final ParentDataElement<SliverWithKeepAliveWidget> childElement = _getChildElement();
       if (childElement != null) {
         // If the child already exists, update it synchronously.
         _updateParentDataOfChild(childElement);
@@ -92,7 +92,7 @@
           if (!mounted) {
             return;
           }
-          final ParentDataElement<SliverMultiBoxAdaptorWidget> childElement = _getChildElement();
+          final ParentDataElement<SliverWithKeepAliveWidget> childElement = _getChildElement();
           assert(childElement != null);
           _updateParentDataOfChild(childElement);
         });
@@ -105,7 +105,7 @@
   ///
   /// While this widget is guaranteed to have a child, this may return null if
   /// the first build of that child has not completed yet.
-  ParentDataElement<SliverMultiBoxAdaptorWidget> _getChildElement() {
+  ParentDataElement<SliverWithKeepAliveWidget> _getChildElement() {
     assert(mounted);
     final Element element = context;
     Element childElement;
@@ -131,11 +131,11 @@
     element.visitChildren((Element child) {
       childElement = child;
     });
-    assert(childElement == null || childElement is ParentDataElement<SliverMultiBoxAdaptorWidget>);
+    assert(childElement == null || childElement is ParentDataElement<SliverWithKeepAliveWidget>);
     return childElement;
   }
 
-  void _updateParentDataOfChild(ParentDataElement<SliverMultiBoxAdaptorWidget> childElement) {
+  void _updateParentDataOfChild(ParentDataElement<SliverWithKeepAliveWidget> childElement) {
     childElement.applyWidgetOutOfTurn(build(context));
   }
 
@@ -396,4 +396,4 @@
       _ensureKeepAlive();
     return null;
   }
-}
+}
\ No newline at end of file
diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart
index f059d04..fa800f8 100644
--- a/packages/flutter/lib/src/widgets/sliver.dart
+++ b/packages/flutter/lib/src/widgets/sliver.dart
@@ -570,10 +570,21 @@
   }
 }
 
+/// A base class for sliver that have [KeepAlive] children.
+abstract class SliverWithKeepAliveWidget extends RenderObjectWidget {
+  /// Initializes fields for subclasses.
+  const SliverWithKeepAliveWidget({
+    Key key,
+  }) : super(key : key);
+
+  @override
+  RenderSliverWithKeepAliveMixin createRenderObject(BuildContext context);
+}
+
 /// A base class for sliver that have multiple box children.
 ///
 /// Helps subclasses build their children lazily using a [SliverChildDelegate].
-abstract class SliverMultiBoxAdaptorWidget extends RenderObjectWidget {
+abstract class SliverMultiBoxAdaptorWidget extends SliverWithKeepAliveWidget {
   /// Initializes fields for subclasses.
   const SliverMultiBoxAdaptorWidget({
     Key key,
@@ -1213,7 +1224,7 @@
 /// Mark a child as needing to stay alive even when it's in a lazy list that
 /// would otherwise remove it.
 ///
-/// This widget is for use in [SliverMultiBoxAdaptorWidget]s, such as
+/// This widget is for use in [SliverWithKeepAliveWidget]s, such as
 /// [SliverGrid] or [SliverList].
 ///
 /// This widget is rarely used directly. The [SliverChildBuilderDelegate] and
@@ -1230,7 +1241,7 @@
 /// In practice, the simplest way to deal with these notifications is to mix
 /// [AutomaticKeepAliveClientMixin] into one's [State]. See the documentation
 /// for that mixin class for details.
-class KeepAlive extends ParentDataWidget<SliverMultiBoxAdaptorWidget> {
+class KeepAlive extends ParentDataWidget<SliverWithKeepAliveWidget> {
   /// Marks a child as needing to remain alive.
   ///
   /// The [child] and [keepAlive] arguments must not be null.
@@ -1249,8 +1260,8 @@
 
   @override
   void applyParentData(RenderObject renderObject) {
-    assert(renderObject.parentData is SliverMultiBoxAdaptorParentData);
-    final SliverMultiBoxAdaptorParentData parentData = renderObject.parentData;
+    assert(renderObject.parentData is KeepAliveParentDataMixin);
+    final KeepAliveParentDataMixin parentData = renderObject.parentData;
     if (parentData.keepAlive != keepAlive) {
       parentData.keepAlive = keepAlive;
       final AbstractNode targetParent = renderObject.parent;
diff --git a/packages/flutter/test/widgets/automatic_keep_alive_test.dart b/packages/flutter/test/widgets/automatic_keep_alive_test.dart
index 06eeec0..404e633 100644
--- a/packages/flutter/test/widgets/automatic_keep_alive_test.dart
+++ b/packages/flutter/test/widgets/automatic_keep_alive_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter/rendering.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
 
@@ -543,6 +544,17 @@
     expect(find.text('FooBar 3'), findsNothing);
     expect(find.text('FooBar 73'), findsOneWidget);
   });
+
+  testWidgets('AutomaticKeepAlive with SliverKeepAliveWidget', (WidgetTester tester) async {
+    // We're just doing a basic test here to make sure that the functionality of
+    // RenderSliverWithKeepAliveMixin doesn't get regressed or deleted. As testing
+    // the full functionality would be cumbersome.
+    final RenderSliverMultiBoxAdaptorAlt alternate = RenderSliverMultiBoxAdaptorAlt();
+    final RenderBox child = RenderBoxKeepAlive();
+    alternate.insert(child);
+
+    expect(alternate.children.length, 1);
+  });
 }
 
 class _AlwaysKeepAlive extends StatefulWidget {
@@ -565,3 +577,57 @@
     );
   }
 }
+
+class RenderBoxKeepAlive extends RenderBox {
+  State<StatefulWidget> createState() => AlwaysKeepAliveRenderBoxState();
+}
+
+class AlwaysKeepAliveRenderBoxState extends State<_AlwaysKeepAlive> with AutomaticKeepAliveClientMixin<_AlwaysKeepAlive> {
+  @override
+  bool get wantKeepAlive => true;
+
+  @override
+  Widget build(BuildContext context) {
+    super.build(context);
+    return Container(
+      height: 48.0,
+      child: const Text('keep me alive'),
+    );
+  }
+}
+
+abstract class KeepAliveParentDataMixinAlt implements KeepAliveParentDataMixin {
+  @override
+  bool keptAlive = false;
+
+  @override
+  bool keepAlive = false;
+}
+
+class RenderSliverMultiBoxAdaptorAlt extends RenderSliver with
+    KeepAliveParentDataMixinAlt,
+    RenderSliverHelpers,
+    RenderSliverWithKeepAliveMixin {
+
+  RenderSliverMultiBoxAdaptorAlt({
+    RenderSliverBoxChildManager childManager
+  }) : _childManager = childManager;
+
+  @protected
+  RenderSliverBoxChildManager get childManager => _childManager;
+  final RenderSliverBoxChildManager _childManager;
+
+  final List<RenderBox> children = <RenderBox>[];
+
+  void insert(RenderBox child, { RenderBox after }) {
+    children.add(child);
+  }
+
+  @override
+  void visitChildren(RenderObjectVisitor visitor) {
+    children.forEach(visitor);
+  }
+
+  @override
+  void performLayout() {}
+}