Unverified Commit 5385132c authored by jslavitz's avatar jslavitz Committed by GitHub

Separate keep alive logic from SliverMultiBox classes (#24192)

* sliver separation and test
parent fc4f9b9e
<?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>
<?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>
......@@ -121,17 +121,35 @@ abstract class RenderSliverBoxChildManager {
/// 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 @@ class SliverMultiBoxAdaptorParentData extends SliverLogicalParentData with Conta
/// * [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 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
}
return children;
}
}
}
\ No newline at end of file
......@@ -80,7 +80,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
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 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
if (!mounted) {
return;
}
final ParentDataElement<SliverMultiBoxAdaptorWidget> childElement = _getChildElement();
final ParentDataElement<SliverWithKeepAliveWidget> childElement = _getChildElement();
assert(childElement != null);
_updateParentDataOfChild(childElement);
});
......@@ -105,7 +105,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
///
/// 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 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
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 @@ mixin AutomaticKeepAliveClientMixin<T extends StatefulWidget> on State<T> {
_ensureKeepAlive();
return null;
}
}
}
\ No newline at end of file
......@@ -570,10 +570,21 @@ class SliverChildListDelegate extends SliverChildDelegate {
}
}
/// 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 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
/// 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 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
/// 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 @@ class KeepAlive extends ParentDataWidget<SliverMultiBoxAdaptorWidget> {
@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;
......
......@@ -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 @@ void main() {
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 _AlwaysKeepAliveState extends State<_AlwaysKeepAlive> with AutomaticKeepAl
);
}
}
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() {}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment