Commit 1c5a9692 authored by Adam Barth's avatar Adam Barth

Merge pull request #2508 from abarth/reparent_multichild

Support reparenting state from MultiChildRenderObjectElement
parents 49c3e183 e8483fb3
...@@ -1605,8 +1605,8 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab ...@@ -1605,8 +1605,8 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
Element oldChild = oldChildren[oldChildrenTop]; Element oldChild = oldChildren[oldChildrenTop];
Widget newWidget = newWidgets[newChildrenTop]; Widget newWidget = newWidgets[newChildrenTop];
assert(oldChild._debugLifecycleState == _ElementLifecycle.active); assert(oldChild == null || oldChild._debugLifecycleState == _ElementLifecycle.active);
if (!Widget.canUpdate(oldChild.widget, newWidget)) if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break; break;
Element newChild = updateChild(oldChild, newWidget, previousChild); Element newChild = updateChild(oldChild, newWidget, previousChild);
assert(newChild._debugLifecycleState == _ElementLifecycle.active); assert(newChild._debugLifecycleState == _ElementLifecycle.active);
...@@ -1620,8 +1620,8 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab ...@@ -1620,8 +1620,8 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
Element oldChild = oldChildren[oldChildrenBottom]; Element oldChild = oldChildren[oldChildrenBottom];
Widget newWidget = newWidgets[newChildrenBottom]; Widget newWidget = newWidgets[newChildrenBottom];
assert(oldChild._debugLifecycleState == _ElementLifecycle.active); assert(oldChild == null || oldChild._debugLifecycleState == _ElementLifecycle.active);
if (!Widget.canUpdate(oldChild.widget, newWidget)) if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break; break;
oldChildrenBottom -= 1; oldChildrenBottom -= 1;
newChildrenBottom -= 1; newChildrenBottom -= 1;
...@@ -1634,11 +1634,13 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab ...@@ -1634,11 +1634,13 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
oldKeyedChildren = new Map<Key, Element>(); oldKeyedChildren = new Map<Key, Element>();
while (oldChildrenTop <= oldChildrenBottom) { while (oldChildrenTop <= oldChildrenBottom) {
Element oldChild = oldChildren[oldChildrenTop]; Element oldChild = oldChildren[oldChildrenTop];
assert(oldChild._debugLifecycleState == _ElementLifecycle.active); assert(oldChild == null || oldChild._debugLifecycleState == _ElementLifecycle.active);
if (oldChild.widget.key != null) if (oldChild != null) {
oldKeyedChildren[oldChild.widget.key] = oldChild; if (oldChild.widget.key != null)
else oldKeyedChildren[oldChild.widget.key] = oldChild;
_deactivateChild(oldChild); else
_deactivateChild(oldChild);
}
oldChildrenTop += 1; oldChildrenTop += 1;
} }
} }
...@@ -1682,6 +1684,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab ...@@ -1682,6 +1684,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
// Update the bottom of the list. // Update the bottom of the list.
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) { while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
Element oldChild = oldChildren[oldChildrenTop]; Element oldChild = oldChildren[oldChildrenTop];
assert(oldChild != null);
assert(oldChild._debugLifecycleState == _ElementLifecycle.active); assert(oldChild._debugLifecycleState == _ElementLifecycle.active);
Widget newWidget = newWidgets[newChildrenTop]; Widget newWidget = newWidgets[newChildrenTop];
assert(Widget.canUpdate(oldChild.widget, newWidget)); assert(Widget.canUpdate(oldChild.widget, newWidget));
...@@ -1826,6 +1829,19 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte ...@@ -1826,6 +1829,19 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
} }
List<Element> _children; List<Element> _children;
// We null out detached children lazily to avoid O(n^2) work walking _children
// repeatedly to remove children.
Set<Element> _detachedChildren;
void _replaceDetachedChildrenWithNull() {
if (_detachedChildren != null && _detachedChildren.isNotEmpty) {
for (int i = 0; i < _children.length; ++i) {
if (_detachedChildren.contains(_children[i]))
_children[i] = null;
}
_detachedChildren.clear();
}
}
void insertChildRenderObject(RenderObject child, Element slot) { void insertChildRenderObject(RenderObject child, Element slot) {
final ContainerRenderObjectMixin renderObject = this.renderObject; final ContainerRenderObjectMixin renderObject = this.renderObject;
...@@ -1865,8 +1881,18 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte ...@@ -1865,8 +1881,18 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
} }
void visitChildren(ElementVisitor visitor) { void visitChildren(ElementVisitor visitor) {
for (Element child in _children) _replaceDetachedChildrenWithNull();
visitor(child); for (Element child in _children) {
if (child != null)
visitor(child);
}
}
bool detachChild(Element child) {
_detachedChildren ??= new Set<Element>();
_detachedChildren.add(child);
_deactivateChild(child);
return true;
} }
void mount(Element parent, dynamic newSlot) { void mount(Element parent, dynamic newSlot) {
...@@ -1883,6 +1909,7 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte ...@@ -1883,6 +1909,7 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
void update(T newWidget) { void update(T newWidget) {
super.update(newWidget); super.update(newWidget);
assert(widget == newWidget); assert(widget == newWidget);
_replaceDetachedChildrenWithNull();
_children = updateChildren(_children, widget.children); _children = updateChildren(_children, widget.children);
} }
} }
......
...@@ -95,6 +95,68 @@ void main() { ...@@ -95,6 +95,68 @@ void main() {
}); });
}); });
test('can reparent state with multichild widgets', () {
testWidgets((WidgetTester tester) {
GlobalKey left = new GlobalKey();
GlobalKey right = new GlobalKey();
StateMarker grandchild = new StateMarker();
tester.pumpWidget(
new Stack(
children: <Widget>[
new StateMarker(key: left),
new StateMarker(
key: right,
child: grandchild
)
]
)
);
(left.currentState as StateMarkerState).marker = "left";
(right.currentState as StateMarkerState).marker = "right";
StateMarkerState grandchildState = tester.findStateByConfig(grandchild);
expect(grandchildState, isNotNull);
grandchildState.marker = "grandchild";
StateMarker newGrandchild = new StateMarker();
tester.pumpWidget(
new Stack(
children: <Widget>[
new StateMarker(
key: right,
child: newGrandchild
),
new StateMarker(key: left)
]
)
);
expect((left.currentState as StateMarkerState).marker, equals("left"));
expect((right.currentState as StateMarkerState).marker, equals("right"));
StateMarkerState newGrandchildState = tester.findStateByConfig(newGrandchild);
expect(newGrandchildState, isNotNull);
expect(newGrandchildState, equals(grandchildState));
expect(newGrandchildState.marker, equals("grandchild"));
tester.pumpWidget(
new Center(
child: new Container(
child: new StateMarker(
key: left,
child: new Container()
)
)
)
);
expect((left.currentState as StateMarkerState).marker, equals("left"));
expect(right.currentState, isNull);
});
});
test('can with scrollable list', () { test('can with scrollable list', () {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
GlobalKey key = new GlobalKey(); GlobalKey key = new GlobalKey();
......
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