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() {}
+}