Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
4caaf9ee
Commit
4caaf9ee
authored
Mar 12, 2016
by
Adam Barth
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2617 from abarth/reparent_ordering
Handle the case of reparenting while updating children
parents
f3dc82ab
9c088362
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
113 additions
and
24 deletions
+113
-24
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+18
-24
reparent_state_test.dart
packages/flutter/test/widget/reparent_state_test.dart
+95
-0
No files found.
packages/flutter/lib/src/widgets/framework.dart
View file @
4caaf9ee
...
@@ -1536,10 +1536,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1536,10 +1536,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
/// Attempts to update the given old children list using the given new
/// Attempts to update the given old children list using the given new
/// widgets, removing obsolete elements and introducing new ones as necessary,
/// widgets, removing obsolete elements and introducing new ones as necessary,
/// and then returns the new child list.
/// and then returns the new child list.
List
<
Element
>
updateChildren
(
List
<
Element
>
oldChildren
,
List
<
Widget
>
newWidgets
)
{
List
<
Element
>
updateChildren
(
List
<
Element
>
oldChildren
,
List
<
Widget
>
newWidgets
,
{
Set
<
Element
>
detachedChildren
}
)
{
assert
(
oldChildren
!=
null
);
assert
(
oldChildren
!=
null
);
assert
(
newWidgets
!=
null
);
assert
(
newWidgets
!=
null
);
Element
replaceWithNullIfDetached
(
Element
child
)
{
return
detachedChildren
!=
null
&&
detachedChildren
.
contains
(
child
)
?
null
:
child
;
}
// This attempts to diff the new child list (this.children) with
// This attempts to diff the new child list (this.children) with
// the old child list (old.children), and update our renderObject
// the old child list (old.children), and update our renderObject
// accordingly.
// accordingly.
...
@@ -1582,7 +1586,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1582,7 +1586,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
// Update the top of the list.
// Update the top of the list.
while
((
oldChildrenTop
<=
oldChildrenBottom
)
&&
(
newChildrenTop
<=
newChildrenBottom
))
{
while
((
oldChildrenTop
<=
oldChildrenBottom
)
&&
(
newChildrenTop
<=
newChildrenBottom
))
{
Element
oldChild
=
oldChildren
[
oldChildrenTop
]
;
Element
oldChild
=
replaceWithNullIfDetached
(
oldChildren
[
oldChildrenTop
])
;
Widget
newWidget
=
newWidgets
[
newChildrenTop
];
Widget
newWidget
=
newWidgets
[
newChildrenTop
];
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
if
(
oldChild
==
null
||
!
Widget
.
canUpdate
(
oldChild
.
widget
,
newWidget
))
if
(
oldChild
==
null
||
!
Widget
.
canUpdate
(
oldChild
.
widget
,
newWidget
))
...
@@ -1597,7 +1601,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1597,7 +1601,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
// Scan the bottom of the list.
// Scan the bottom of the list.
while
((
oldChildrenTop
<=
oldChildrenBottom
)
&&
(
newChildrenTop
<=
newChildrenBottom
))
{
while
((
oldChildrenTop
<=
oldChildrenBottom
)
&&
(
newChildrenTop
<=
newChildrenBottom
))
{
Element
oldChild
=
oldChildren
[
oldChildrenBottom
]
;
Element
oldChild
=
replaceWithNullIfDetached
(
oldChildren
[
oldChildrenBottom
])
;
Widget
newWidget
=
newWidgets
[
newChildrenBottom
];
Widget
newWidget
=
newWidgets
[
newChildrenBottom
];
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
if
(
oldChild
==
null
||
!
Widget
.
canUpdate
(
oldChild
.
widget
,
newWidget
))
if
(
oldChild
==
null
||
!
Widget
.
canUpdate
(
oldChild
.
widget
,
newWidget
))
...
@@ -1612,7 +1616,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1612,7 +1616,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
if
(
haveOldChildren
)
{
if
(
haveOldChildren
)
{
oldKeyedChildren
=
new
Map
<
Key
,
Element
>();
oldKeyedChildren
=
new
Map
<
Key
,
Element
>();
while
(
oldChildrenTop
<=
oldChildrenBottom
)
{
while
(
oldChildrenTop
<=
oldChildrenBottom
)
{
Element
oldChild
=
oldChildren
[
oldChildrenTop
]
;
Element
oldChild
=
replaceWithNullIfDetached
(
oldChildren
[
oldChildrenTop
])
;
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
assert
(
oldChild
==
null
||
oldChild
.
_debugLifecycleState
==
_ElementLifecycle
.
active
);
if
(
oldChild
!=
null
)
{
if
(
oldChild
!=
null
)
{
if
(
oldChild
.
widget
.
key
!=
null
)
if
(
oldChild
.
widget
.
key
!=
null
)
...
@@ -1663,7 +1667,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1663,7 +1667,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
(
replaceWithNullIfDetached
(
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
));
...
@@ -1678,9 +1682,11 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
...
@@ -1678,9 +1682,11 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Buildab
// clean up any of the remaining middle nodes from the old list
// clean up any of the remaining middle nodes from the old list
if
(
haveOldChildren
&&
oldKeyedChildren
.
isNotEmpty
)
{
if
(
haveOldChildren
&&
oldKeyedChildren
.
isNotEmpty
)
{
for
(
Element
oldChild
in
oldKeyedChildren
.
values
)
for
(
Element
oldChild
in
oldKeyedChildren
.
values
)
{
if
(
detachedChildren
==
null
||
!
detachedChildren
.
contains
(
oldChild
))
_deactivateChild
(
oldChild
);
_deactivateChild
(
oldChild
);
}
}
}
return
newChildren
;
return
newChildren
;
}
}
...
@@ -1808,19 +1814,9 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
...
@@ -1808,19 +1814,9 @@ 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
// We
keep a set of detached children
to avoid O(n^2) work walking _children
// repeatedly to remove children.
// repeatedly to remove children.
Set
<
Element
>
_detachedChildren
;
final
Set
<
Element
>
_detachedChildren
=
new
HashSet
<
Element
>();
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
;
...
@@ -1860,15 +1856,13 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
...
@@ -1860,15 +1856,13 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
}
}
void
visitChildren
(
ElementVisitor
visitor
)
{
void
visitChildren
(
ElementVisitor
visitor
)
{
_replaceDetachedChildrenWithNull
();
for
(
Element
child
in
_children
)
{
for
(
Element
child
in
_children
)
{
if
(
child
!=
null
)
if
(
!
_detachedChildren
.
contains
(
child
)
)
visitor
(
child
);
visitor
(
child
);
}
}
}
}
bool
detachChild
(
Element
child
)
{
bool
detachChild
(
Element
child
)
{
_detachedChildren
??=
new
Set
<
Element
>();
_detachedChildren
.
add
(
child
);
_detachedChildren
.
add
(
child
);
_deactivateChild
(
child
);
_deactivateChild
(
child
);
return
true
;
return
true
;
...
@@ -1888,8 +1882,8 @@ class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> exte
...
@@ -1888,8 +1882,8 @@ 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
,
detachedChildren:
_detachedChildren
);
_
children
=
updateChildren
(
_children
,
widget
.
children
);
_
detachedChildren
.
clear
(
);
}
}
}
}
...
...
packages/flutter/test/widget/reparent_state_test.dart
View file @
4caaf9ee
...
@@ -196,4 +196,99 @@ void main() {
...
@@ -196,4 +196,99 @@ void main() {
expect
(
keyState
.
marker
,
equals
(
"marked"
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
});
});
});
});
test
(
'Reparent during update children'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
key
=
new
GlobalKey
();
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
StateMarker
(
key:
key
),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
StateMarkerState
keyState
=
key
.
currentState
;
keyState
.
marker
=
"marked"
;
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
),
new
StateMarker
(
key:
key
),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
StateMarker
(
key:
key
),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
});
});
test
(
'Reparent to child during update children'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
key
=
new
GlobalKey
();
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
),
new
StateMarker
(
key:
key
),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
StateMarkerState
keyState
=
key
.
currentState
;
keyState
.
marker
=
"marked"
;
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
,
child:
new
StateMarker
(
key:
key
)),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
),
new
StateMarker
(
key:
key
),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
),
new
Container
(
width:
100.0
,
height:
100.0
,
child:
new
StateMarker
(
key:
key
)),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
tester
.
pumpWidget
(
new
Stack
(
children:
<
Widget
>[
new
Container
(
width:
100.0
,
height:
100.0
),
new
StateMarker
(
key:
key
),
new
Container
(
width:
100.0
,
height:
100.0
),
]
));
expect
(
key
.
currentState
,
equals
(
keyState
));
expect
(
keyState
.
marker
,
equals
(
"marked"
));
});
});
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment