Merge pull request #2508 from abarth/reparent_multichild
Support reparenting state from MultiChildRenderObjectElement
This commit is contained in:
commit
1c5a9692d8
@ -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
|
|||||||
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
|
|||||||
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
|
|||||||
// 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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user