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
52795630
Commit
52795630
authored
Jun 28, 2017
by
Ian Hickson
Committed by
GitHub
Jun 28, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Keep-alive for widgets in lazy lists (#11010)
parent
57746f38
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
905 additions
and
61 deletions
+905
-61
sliver_fixed_extent_list.dart
...s/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
+3
-2
sliver_grid.dart
packages/flutter/lib/src/rendering/sliver_grid.dart
+3
-2
sliver_multi_box_adaptor.dart
...s/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
+179
-39
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+3
-2
scroll_view.dart
packages/flutter/lib/src/widgets/scroll_view.dart
+56
-6
sliver.dart
packages/flutter/lib/src/widgets/sliver.dart
+77
-6
slivers_block_test.dart
packages/flutter/test/rendering/slivers_block_test.dart
+19
-0
keep_alive_test.dart
packages/flutter/test/widgets/keep_alive_test.dart
+561
-0
list_view_viewporting_test.dart
...ages/flutter/test/widgets/list_view_viewporting_test.dart
+3
-3
sliver_fill_viewport_test.dart
packages/flutter/test/widgets/sliver_fill_viewport_test.dart
+1
-1
No files found.
packages/flutter/lib/src/rendering/sliver_fixed_extent_list.dart
View file @
52795630
...
@@ -130,8 +130,9 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
...
@@ -130,8 +130,9 @@ abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAda
final
int
oldLastIndex
=
indexOf
(
lastChild
);
final
int
oldLastIndex
=
indexOf
(
lastChild
);
final
int
leadingGarbage
=
(
firstIndex
-
oldFirstIndex
).
clamp
(
0
,
childCount
);
final
int
leadingGarbage
=
(
firstIndex
-
oldFirstIndex
).
clamp
(
0
,
childCount
);
final
int
trailingGarbage
=
targetLastIndex
==
null
?
0
:
(
oldLastIndex
-
targetLastIndex
).
clamp
(
0
,
childCount
);
final
int
trailingGarbage
=
targetLastIndex
==
null
?
0
:
(
oldLastIndex
-
targetLastIndex
).
clamp
(
0
,
childCount
);
if
(
leadingGarbage
+
trailingGarbage
>
0
)
collectGarbage
(
leadingGarbage
,
trailingGarbage
);
collectGarbage
(
leadingGarbage
,
trailingGarbage
);
}
else
{
collectGarbage
(
0
,
0
);
}
}
if
(
firstChild
==
null
)
{
if
(
firstChild
==
null
)
{
...
...
packages/flutter/lib/src/rendering/sliver_grid.dart
View file @
52795630
...
@@ -507,8 +507,9 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
...
@@ -507,8 +507,9 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
final
int
oldLastIndex
=
indexOf
(
lastChild
);
final
int
oldLastIndex
=
indexOf
(
lastChild
);
final
int
leadingGarbage
=
(
firstIndex
-
oldFirstIndex
).
clamp
(
0
,
childCount
);
final
int
leadingGarbage
=
(
firstIndex
-
oldFirstIndex
).
clamp
(
0
,
childCount
);
final
int
trailingGarbage
=
targetLastIndex
==
null
?
0
:
(
oldLastIndex
-
targetLastIndex
).
clamp
(
0
,
childCount
);
final
int
trailingGarbage
=
targetLastIndex
==
null
?
0
:
(
oldLastIndex
-
targetLastIndex
).
clamp
(
0
,
childCount
);
if
(
leadingGarbage
+
trailingGarbage
>
0
)
collectGarbage
(
leadingGarbage
,
trailingGarbage
);
collectGarbage
(
leadingGarbage
,
trailingGarbage
);
}
else
{
collectGarbage
(
0
,
0
);
}
}
final
SliverGridGeometry
firstChildGridGeometry
=
layout
.
getGeometryForChildIndex
(
firstIndex
);
final
SliverGridGeometry
firstChildGridGeometry
=
layout
.
getGeometryForChildIndex
(
firstIndex
);
...
...
packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
View file @
52795630
...
@@ -111,8 +111,15 @@ class SliverMultiBoxAdaptorParentData extends SliverLogicalParentData with Conta
...
@@ -111,8 +111,15 @@ class SliverMultiBoxAdaptorParentData extends SliverLogicalParentData with Conta
/// The index of this child according to the [RenderSliverBoxChildManager].
/// The index of this child according to the [RenderSliverBoxChildManager].
int
index
;
int
index
;
/// Whether to keep the child alive even when it is no longer visible.
bool
keepAlive
=
false
;
/// Whether the widget is currently in the
/// [RenderSliverMultiBoxAdaptor._keepAliveBucket].
bool
_keptAlive
=
false
;
@override
@override
String
toString
()
=>
'index=
$index
;
${super.toString()}
'
;
String
toString
()
=>
'index=
$index
;
${
keepAlive == true ? "keepAlive; " : ""}${
super.toString()}
'
;
}
}
/// A sliver with multiple box children.
/// A sliver with multiple box children.
...
@@ -168,10 +175,15 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -168,10 +175,15 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
RenderSliverBoxChildManager
get
childManager
=>
_childManager
;
RenderSliverBoxChildManager
get
childManager
=>
_childManager
;
final
RenderSliverBoxChildManager
_childManager
;
final
RenderSliverBoxChildManager
_childManager
;
/// The nodes being kept alive despite not being visible.
final
Map
<
int
,
RenderBox
>
_keepAliveBucket
=
<
int
,
RenderBox
>{};
@override
@override
void
adoptChild
(
RenderObject
child
)
{
void
adoptChild
(
RenderObject
child
)
{
super
.
adoptChild
(
child
);
super
.
adoptChild
(
child
);
childManager
.
didAdoptChild
(
child
);
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
if
(!
childParentData
.
_keptAlive
)
childManager
.
didAdoptChild
(
child
);
}
}
bool
_debugAssertChildListLocked
()
=>
childManager
.
debugAssertChildListLocked
();
bool
_debugAssertChildListLocked
()
=>
childManager
.
debugAssertChildListLocked
();
...
@@ -192,64 +204,139 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -192,64 +204,139 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
});
});
}
}
@override
void
remove
(
RenderBox
child
)
{
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
if
(!
childParentData
.
_keptAlive
)
{
super
.
remove
(
child
);
return
;
}
assert
(
_keepAliveBucket
[
childParentData
.
index
]
==
child
);
_keepAliveBucket
.
remove
(
childParentData
.
index
);
dropChild
(
child
);
}
@override
void
removeAll
()
{
super
.
removeAll
();
for
(
RenderBox
child
in
_keepAliveBucket
.
values
)
dropChild
(
child
);
_keepAliveBucket
.
clear
();
}
void
_createOrObtainChild
(
int
index
,
{
RenderBox
after
})
{
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
assert
(
constraints
==
this
.
constraints
);
if
(
_keepAliveBucket
.
containsKey
(
index
))
{
final
RenderBox
child
=
_keepAliveBucket
.
remove
(
index
);
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
assert
(
childParentData
.
_keptAlive
);
dropChild
(
child
);
child
.
parentData
=
childParentData
;
insert
(
child
,
after:
after
);
childParentData
.
_keptAlive
=
false
;
}
else
{
_childManager
.
createChild
(
index
,
after:
after
);
}
});
}
void
_destroyOrCacheChild
(
RenderBox
child
)
{
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
if
(
childParentData
.
keepAlive
)
{
assert
(!
childParentData
.
_keptAlive
);
remove
(
child
);
_keepAliveBucket
[
childParentData
.
index
]
=
child
;
child
.
parentData
=
childParentData
;
super
.
adoptChild
(
child
);
childParentData
.
_keptAlive
=
true
;
}
else
{
assert
(
child
.
parent
==
this
);
_childManager
.
removeChild
(
child
);
assert
(
child
.
parent
==
null
);
}
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
for
(
RenderBox
child
in
_keepAliveBucket
.
values
)
child
.
attach
(
owner
);
}
@override
void
detach
()
{
super
.
detach
();
for
(
RenderBox
child
in
_keepAliveBucket
.
values
)
child
.
detach
();
}
@override
void
redepthChildren
()
{
super
.
redepthChildren
();
for
(
RenderBox
child
in
_keepAliveBucket
.
values
)
redepthChild
(
child
);
}
@override
void
visitChildren
(
RenderObjectVisitor
visitor
)
{
super
.
visitChildren
(
visitor
);
for
(
RenderBox
child
in
_keepAliveBucket
.
values
)
visitor
(
child
);
}
/// Called during layout to create and add the child with the given index and
/// Called during layout to create and add the child with the given index and
/// scroll offset.
/// scroll offset.
///
///
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// the child.
/// the child if necessary. The child may instead be obtained from a cache;
/// see [SliverMultiBoxAdaptorParentData.keepAlive].
///
///
/// Returns false if
createChild did not add any child, otherwise returns
/// Returns false if
there was no cached child and `createChild` did not add
/// true.
///
any child, otherwise returns
true.
///
///
/// Does not layout the new child.
/// Does not layout the new child.
///
///
/// When this is called, there are no children, so no children can be removed
/// When this is called, there are no visible children, so no children can be
/// during the call to createChild. No child should be added during that call
/// removed during the call to `createChild`. No child should be added during
/// either, except for the one that is created and returned by createChild.
/// that call either, except for the one that is created and returned by
/// `createChild`.
@protected
@protected
bool
addInitialChild
({
int
index:
0
,
double
layoutOffset:
0.0
})
{
bool
addInitialChild
({
int
index:
0
,
double
layoutOffset:
0.0
})
{
assert
(
_debugAssertChildListLocked
());
assert
(
_debugAssertChildListLocked
());
assert
(
firstChild
==
null
);
assert
(
firstChild
==
null
);
bool
result
;
_createOrObtainChild
(
index
,
after:
null
);
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
if
(
firstChild
!=
null
)
{
assert
(
constraints
==
this
.
constraints
);
assert
(
firstChild
==
lastChild
);
_childManager
.
createChild
(
index
,
after:
null
);
assert
(
indexOf
(
firstChild
)
==
index
);
if
(
firstChild
!=
null
)
{
final
SliverMultiBoxAdaptorParentData
firstChildParentData
=
firstChild
.
parentData
;
assert
(
firstChild
==
lastChild
);
firstChildParentData
.
layoutOffset
=
layoutOffset
;
assert
(
indexOf
(
firstChild
)
==
index
);
return
true
;
final
SliverMultiBoxAdaptorParentData
firstChildParentData
=
firstChild
.
parentData
;
}
firstChildParentData
.
layoutOffset
=
layoutOffset
;
childManager
.
setDidUnderflow
(
true
);
result
=
true
;
return
false
;
}
else
{
childManager
.
setDidUnderflow
(
true
);
result
=
false
;
}
});
return
result
;
}
}
/// Called during layout to create, add, and layout the child before
/// Called during layout to create, add, and layout the child before
/// [firstChild].
/// [firstChild].
///
///
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// the child.
/// the child if necessary. The child may instead be obtained from a cache;
/// see [SliverMultiBoxAdaptorParentData.keepAlive].
///
///
/// Returns the new child or null if no child
is creat
ed.
/// Returns the new child or null if no child
was obtain
ed.
///
///
/// The child that was previously the first child, as well as any subsequent
/// The child that was previously the first child, as well as any subsequent
/// children, may be removed by this call if they have not yet been laid out
/// children, may be removed by this call if they have not yet been laid out
/// during this layout pass. No child should be added during that call except
/// during this layout pass. No child should be added during that call except
/// for the one that is created and returned by
createChild
.
/// for the one that is created and returned by
`createChild`
.
@protected
@protected
RenderBox
insertAndLayoutLeadingChild
(
BoxConstraints
childConstraints
,
{
RenderBox
insertAndLayoutLeadingChild
(
BoxConstraints
childConstraints
,
{
bool
parentUsesSize:
false
,
bool
parentUsesSize:
false
,
})
{
})
{
assert
(
_debugAssertChildListLocked
());
assert
(
_debugAssertChildListLocked
());
final
int
index
=
indexOf
(
firstChild
)
-
1
;
final
int
index
=
indexOf
(
firstChild
)
-
1
;
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
_createOrObtainChild
(
index
,
after:
null
);
assert
(
constraints
==
this
.
constraints
);
_childManager
.
createChild
(
index
,
after:
null
);
});
if
(
indexOf
(
firstChild
)
==
index
)
{
if
(
indexOf
(
firstChild
)
==
index
)
{
firstChild
.
layout
(
childConstraints
,
parentUsesSize:
parentUsesSize
);
firstChild
.
layout
(
childConstraints
,
parentUsesSize:
parentUsesSize
);
return
firstChild
;
return
firstChild
;
...
@@ -262,7 +349,8 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -262,7 +349,8 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
/// the given child.
/// the given child.
///
///
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// Calls [RenderSliverBoxChildManager.createChild] to actually create and add
/// the child.
/// the child if necessary. The child may instead be obtained from a cache;
/// see [SliverMultiBoxAdaptorParentData.keepAlive].
///
///
/// Returns the new child. It is the responsibility of the caller to configure
/// Returns the new child. It is the responsibility of the caller to configure
/// the child's scroll offset.
/// the child's scroll offset.
...
@@ -277,13 +365,9 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -277,13 +365,9 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
assert
(
_debugAssertChildListLocked
());
assert
(
_debugAssertChildListLocked
());
assert
(
after
!=
null
);
assert
(
after
!=
null
);
final
int
index
=
indexOf
(
after
)
+
1
;
final
int
index
=
indexOf
(
after
)
+
1
;
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
_createOrObtainChild
(
index
,
after:
after
);
assert
(
constraints
==
this
.
constraints
);
_childManager
.
createChild
(
index
,
after:
after
);
});
final
RenderBox
child
=
childAfter
(
after
);
final
RenderBox
child
=
childAfter
(
after
);
if
(
child
!=
null
&&
indexOf
(
child
)
==
index
)
{
if
(
child
!=
null
&&
indexOf
(
child
)
==
index
)
{
assert
(
indexOf
(
child
)
==
index
);
child
.
layout
(
childConstraints
,
parentUsesSize:
parentUsesSize
);
child
.
layout
(
childConstraints
,
parentUsesSize:
parentUsesSize
);
return
child
;
return
child
;
}
}
...
@@ -293,19 +377,37 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -293,19 +377,37 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
/// Called after layout with the number of children that can be garbage
/// Called after layout with the number of children that can be garbage
/// collected at the head and tail of the child list.
/// collected at the head and tail of the child list.
///
/// Children whose [SliverMultiBoxAdaptorParentData.keepAlive] property is
/// set to true will be removed to a cache instead of being dropped.
///
/// This method also collects any children that were previously kept alive but
/// are now no longer necessary. As such, it should be called every time
/// [performLayout] is run, even if the arguments are both zero.
@protected
@protected
void
collectGarbage
(
int
leadingGarbage
,
int
trailingGarbage
)
{
void
collectGarbage
(
int
leadingGarbage
,
int
trailingGarbage
)
{
assert
(
_debugAssertChildListLocked
());
assert
(
_debugAssertChildListLocked
());
assert
(
childCount
>=
leadingGarbage
+
trailingGarbage
);
assert
(
childCount
>=
leadingGarbage
+
trailingGarbage
);
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
invokeLayoutCallback
<
SliverConstraints
>((
SliverConstraints
constraints
)
{
while
(
leadingGarbage
>
0
)
{
while
(
leadingGarbage
>
0
)
{
_
childManager
.
remov
eChild
(
firstChild
);
_
destroyOrCach
eChild
(
firstChild
);
leadingGarbage
-=
1
;
leadingGarbage
-=
1
;
}
}
while
(
trailingGarbage
>
0
)
{
while
(
trailingGarbage
>
0
)
{
_
childManager
.
remov
eChild
(
lastChild
);
_
destroyOrCach
eChild
(
lastChild
);
trailingGarbage
-=
1
;
trailingGarbage
-=
1
;
}
}
// Ask the child manager to remove the children that are no longer being
// kept alive. (This should cause _keepAliveBucket to change, so we have
// to prepare our list ahead of time.)
_keepAliveBucket
.
values
.
where
((
RenderBox
child
)
{
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
return
!
childParentData
.
keepAlive
;
}).
toList
().
forEach
(
_childManager
.
removeChild
);
assert
(
_keepAliveBucket
.
values
.
where
((
RenderBox
child
)
{
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
return
!
childParentData
.
keepAlive
;
}).
isEmpty
);
});
});
}
}
...
@@ -442,4 +544,42 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -442,4 +544,42 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
});
});
return
true
;
return
true
;
}
}
@override
String
debugDescribeChildren
(
String
prefix
)
{
StringBuffer
result
;
if
(
firstChild
!=
null
)
{
result
=
new
StringBuffer
()
..
write
(
prefix
)
..
write
(
'
\
u2502
\n
'
);
RenderBox
child
=
firstChild
;
while
(
child
!=
lastChild
)
{
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
result
.
write
(
child
.
toStringDeep
(
"
$prefix
\
u251C
\
u2500child with index
${childParentData.index}
: "
,
"
$prefix
\
u2502"
));
child
=
childParentData
.
nextSibling
;
}
if
(
child
!=
null
)
{
assert
(
child
==
lastChild
);
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
if
(
_keepAliveBucket
.
isEmpty
)
{
result
.
write
(
child
.
toStringDeep
(
"
$prefix
\
u2514
\
u2500child with index
${childParentData.index}
: "
,
"
$prefix
"
));
}
else
{
result
.
write
(
child
.
toStringDeep
(
"
$prefix
\
u251C
\
u2500child with index
${childParentData.index}
: "
,
"
$prefix
\
u254E"
));
}
}
}
if
(
_keepAliveBucket
.
isNotEmpty
)
{
result
??=
new
StringBuffer
()
..
write
(
prefix
)
..
write
(
'
\
u254E
\n
'
);
final
List
<
int
>
indices
=
_keepAliveBucket
.
keys
.
toList
()..
sort
();
final
int
lastIndex
=
indices
.
removeLast
();
if
(
indices
.
isNotEmpty
)
{
for
(
int
index
in
indices
)
result
.
write
(
_keepAliveBucket
[
index
].
toStringDeep
(
"
$prefix
\
u251C
\
u2500child with index
$index
(kept alive offstage): "
,
"
$prefix
\
u254E"
));
}
result
.
write
(
_keepAliveBucket
[
lastIndex
].
toStringDeep
(
"
$prefix
\
u2514
\
u2500child with index
$lastIndex
(kept alive offstage): "
,
"
$prefix
"
));
}
return
result
?.
toString
()
??
''
;
}
}
}
packages/flutter/lib/src/widgets/basic.dart
View file @
52795630
...
@@ -1201,8 +1201,9 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
...
@@ -1201,8 +1201,9 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
/// Meta data for identifying children in a [CustomMultiChildLayout].
/// Meta data for identifying children in a [CustomMultiChildLayout].
///
///
/// The [MultiChildLayoutDelegate] hasChild, layoutChild, and positionChild
/// The [MultiChildLayoutDelegate.hasChild],
/// methods use these identifiers.
/// [MultiChildLayoutDelegate.layoutChild], and
/// [MultiChildLayoutDelegate.positionChild] methods use these identifiers.
class
LayoutId
extends
ParentDataWidget
<
CustomMultiChildLayout
>
{
class
LayoutId
extends
ParentDataWidget
<
CustomMultiChildLayout
>
{
/// Marks a child with a layout identifier.
/// Marks a child with a layout identifier.
///
///
...
...
packages/flutter/lib/src/widgets/scroll_view.dart
View file @
52795630
...
@@ -514,6 +514,10 @@ class ListView extends BoxScrollView {
...
@@ -514,6 +514,10 @@ class ListView extends BoxScrollView {
///
///
/// It is usually more efficient to create children on demand using [new
/// It is usually more efficient to create children on demand using [new
/// ListView.builder].
/// ListView.builder].
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property and must not be
/// null.
ListView
({
ListView
({
Key
key
,
Key
key
,
Axis
scrollDirection:
Axis
.
vertical
,
Axis
scrollDirection:
Axis
.
vertical
,
...
@@ -524,8 +528,12 @@ class ListView extends BoxScrollView {
...
@@ -524,8 +528,12 @@ class ListView extends BoxScrollView {
bool
shrinkWrap:
false
,
bool
shrinkWrap:
false
,
EdgeInsets
padding
,
EdgeInsets
padding
,
this
.
itemExtent
,
this
.
itemExtent
,
bool
addRepaintBoundaries:
true
,
List
<
Widget
>
children:
const
<
Widget
>[],
List
<
Widget
>
children:
const
<
Widget
>[],
})
:
childrenDelegate
=
new
SliverChildListDelegate
(
children
),
super
(
})
:
childrenDelegate
=
new
SliverChildListDelegate
(
children
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
reverse:
reverse
,
reverse:
reverse
,
...
@@ -554,6 +562,10 @@ class ListView extends BoxScrollView {
...
@@ -554,6 +562,10 @@ class ListView extends BoxScrollView {
/// [ListView] itself is created, it is more efficient to use [new ListView].
/// [ListView] itself is created, it is more efficient to use [new ListView].
/// Even more efficient, however, is to create the instances on demand using
/// Even more efficient, however, is to create the instances on demand using
/// this constructor's `itemBuilder` callback.
/// this constructor's `itemBuilder` callback.
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildBuilderDelegate.addRepaintBoundaries] property and must not be
/// null.
ListView
.
builder
({
ListView
.
builder
({
Key
key
,
Key
key
,
Axis
scrollDirection:
Axis
.
vertical
,
Axis
scrollDirection:
Axis
.
vertical
,
...
@@ -566,7 +578,12 @@ class ListView extends BoxScrollView {
...
@@ -566,7 +578,12 @@ class ListView extends BoxScrollView {
this
.
itemExtent
,
this
.
itemExtent
,
@required
IndexedWidgetBuilder
itemBuilder
,
@required
IndexedWidgetBuilder
itemBuilder
,
int
itemCount
,
int
itemCount
,
})
:
childrenDelegate
=
new
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
),
super
(
bool
addRepaintBoundaries:
true
,
})
:
childrenDelegate
=
new
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
reverse:
reverse
,
reverse:
reverse
,
...
@@ -765,6 +782,10 @@ class GridView extends BoxScrollView {
...
@@ -765,6 +782,10 @@ class GridView extends BoxScrollView {
/// [SliverGridDelegate].
/// [SliverGridDelegate].
///
///
/// The [gridDelegate] argument must not be null.
/// The [gridDelegate] argument must not be null.
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property and must not be
/// null.
GridView
({
GridView
({
Key
key
,
Key
key
,
Axis
scrollDirection:
Axis
.
vertical
,
Axis
scrollDirection:
Axis
.
vertical
,
...
@@ -775,9 +796,13 @@ class GridView extends BoxScrollView {
...
@@ -775,9 +796,13 @@ class GridView extends BoxScrollView {
bool
shrinkWrap:
false
,
bool
shrinkWrap:
false
,
EdgeInsets
padding
,
EdgeInsets
padding
,
@required
this
.
gridDelegate
,
@required
this
.
gridDelegate
,
bool
addRepaintBoundaries:
true
,
List
<
Widget
>
children:
const
<
Widget
>[],
List
<
Widget
>
children:
const
<
Widget
>[],
})
:
assert
(
gridDelegate
!=
null
),
})
:
assert
(
gridDelegate
!=
null
),
childrenDelegate
=
new
SliverChildListDelegate
(
children
),
childrenDelegate
=
new
SliverChildListDelegate
(
children
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
...
@@ -802,6 +827,10 @@ class GridView extends BoxScrollView {
...
@@ -802,6 +827,10 @@ class GridView extends BoxScrollView {
/// zero and less than `itemCount`.
/// zero and less than `itemCount`.
///
///
/// The [gridDelegate] argument must not be null.
/// The [gridDelegate] argument must not be null.
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildBuilderDelegate.addRepaintBoundaries] property and must not be
/// null.
GridView
.
builder
({
GridView
.
builder
({
Key
key
,
Key
key
,
Axis
scrollDirection:
Axis
.
vertical
,
Axis
scrollDirection:
Axis
.
vertical
,
...
@@ -814,8 +843,13 @@ class GridView extends BoxScrollView {
...
@@ -814,8 +843,13 @@ class GridView extends BoxScrollView {
@required
this
.
gridDelegate
,
@required
this
.
gridDelegate
,
@required
IndexedWidgetBuilder
itemBuilder
,
@required
IndexedWidgetBuilder
itemBuilder
,
int
itemCount
,
int
itemCount
,
bool
addRepaintBoundaries:
true
,
})
:
assert
(
gridDelegate
!=
null
),
})
:
assert
(
gridDelegate
!=
null
),
childrenDelegate
=
new
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
),
childrenDelegate
=
new
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
...
@@ -863,6 +897,10 @@ class GridView extends BoxScrollView {
...
@@ -863,6 +897,10 @@ class GridView extends BoxScrollView {
///
///
/// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
/// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
///
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property and must not be
/// null.
///
/// See also:
/// See also:
///
///
/// * [new SliverGrid.count], the equivalent constructor for [SliverGrid].
/// * [new SliverGrid.count], the equivalent constructor for [SliverGrid].
...
@@ -879,6 +917,7 @@ class GridView extends BoxScrollView {
...
@@ -879,6 +917,7 @@ class GridView extends BoxScrollView {
double
mainAxisSpacing:
0.0
,
double
mainAxisSpacing:
0.0
,
double
crossAxisSpacing:
0.0
,
double
crossAxisSpacing:
0.0
,
double
childAspectRatio:
1.0
,
double
childAspectRatio:
1.0
,
bool
addRepaintBoundaries:
true
,
List
<
Widget
>
children:
const
<
Widget
>[],
List
<
Widget
>
children:
const
<
Widget
>[],
})
:
gridDelegate
=
new
SliverGridDelegateWithFixedCrossAxisCount
(
})
:
gridDelegate
=
new
SliverGridDelegateWithFixedCrossAxisCount
(
crossAxisCount:
crossAxisCount
,
crossAxisCount:
crossAxisCount
,
...
@@ -886,7 +925,10 @@ class GridView extends BoxScrollView {
...
@@ -886,7 +925,10 @@ class GridView extends BoxScrollView {
crossAxisSpacing:
crossAxisSpacing
,
crossAxisSpacing:
crossAxisSpacing
,
childAspectRatio:
childAspectRatio
,
childAspectRatio:
childAspectRatio
,
),
),
childrenDelegate
=
new
SliverChildListDelegate
(
children
),
super
(
childrenDelegate
=
new
SliverChildListDelegate
(
children
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
reverse:
reverse
,
reverse:
reverse
,
...
@@ -902,6 +944,10 @@ class GridView extends BoxScrollView {
...
@@ -902,6 +944,10 @@ class GridView extends BoxScrollView {
///
///
/// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
/// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
///
///
/// The `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property and must not be
/// null.
///
/// See also:
/// See also:
///
///
/// * [new SliverGrid.extent], the equivalent constructor for [SliverGrid].
/// * [new SliverGrid.extent], the equivalent constructor for [SliverGrid].
...
@@ -918,6 +964,7 @@ class GridView extends BoxScrollView {
...
@@ -918,6 +964,7 @@ class GridView extends BoxScrollView {
double
mainAxisSpacing:
0.0
,
double
mainAxisSpacing:
0.0
,
double
crossAxisSpacing:
0.0
,
double
crossAxisSpacing:
0.0
,
double
childAspectRatio:
1.0
,
double
childAspectRatio:
1.0
,
bool
addRepaintBoundaries:
true
,
List
<
Widget
>
children:
const
<
Widget
>[],
List
<
Widget
>
children:
const
<
Widget
>[],
})
:
gridDelegate
=
new
SliverGridDelegateWithMaxCrossAxisExtent
(
})
:
gridDelegate
=
new
SliverGridDelegateWithMaxCrossAxisExtent
(
maxCrossAxisExtent:
maxCrossAxisExtent
,
maxCrossAxisExtent:
maxCrossAxisExtent
,
...
@@ -925,7 +972,10 @@ class GridView extends BoxScrollView {
...
@@ -925,7 +972,10 @@ class GridView extends BoxScrollView {
crossAxisSpacing:
crossAxisSpacing
,
crossAxisSpacing:
crossAxisSpacing
,
childAspectRatio:
childAspectRatio
,
childAspectRatio:
childAspectRatio
,
),
),
childrenDelegate
=
new
SliverChildListDelegate
(
children
),
super
(
childrenDelegate
=
new
SliverChildListDelegate
(
children
,
addRepaintBoundaries:
addRepaintBoundaries
,
),
super
(
key:
key
,
key:
key
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
reverse:
reverse
,
reverse:
reverse
,
...
...
packages/flutter/lib/src/widgets/sliver.dart
View file @
52795630
...
@@ -115,8 +115,11 @@ abstract class SliverChildDelegate {
...
@@ -115,8 +115,11 @@ abstract class SliverChildDelegate {
///
///
/// Many slivers lazily construct their box children to avoid creating more
/// Many slivers lazily construct their box children to avoid creating more
/// children than are visible through the [Viewport]. This delegate provides
/// children than are visible through the [Viewport]. This delegate provides
/// children using an [IndexedWidgetBuilder] callback. The widgets returned from
/// children using an [IndexedWidgetBuilder] callback, so that the children do
/// the builder callback are wrapped in [RepaintBoundary] widgets.
/// not even have to be built until they are displayed.
///
/// The widgets returned from the builder callback are automatically wrapped in
/// [RepaintBoundary] widgets if [addRepaintBoundaries] is true (the default).
///
///
/// See also:
/// See also:
///
///
...
@@ -124,8 +127,15 @@ abstract class SliverChildDelegate {
...
@@ -124,8 +127,15 @@ abstract class SliverChildDelegate {
/// of children.
/// of children.
class
SliverChildBuilderDelegate
extends
SliverChildDelegate
{
class
SliverChildBuilderDelegate
extends
SliverChildDelegate
{
/// Creates a delegate that supplies children for slivers using the given
/// Creates a delegate that supplies children for slivers using the given
/// builder callback
/// builder callback.
const
SliverChildBuilderDelegate
(
this
.
builder
,
{
this
.
childCount
});
///
/// The [builder] and [addRepaintBoundaries] arguments must not be null.
const
SliverChildBuilderDelegate
(
this
.
builder
,
{
this
.
childCount
,
this
.
addRepaintBoundaries
:
true
,
})
:
assert
(
builder
!=
null
),
assert
(
addRepaintBoundaries
!=
null
);
/// Called to build children for the sliver.
/// Called to build children for the sliver.
///
///
...
@@ -145,6 +155,17 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -145,6 +155,17 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
/// [builder] returns null.
/// [builder] returns null.
final
int
childCount
;
final
int
childCount
;
/// Whether to wrap each child in a [RepaintBoundary].
///
/// Typically, children in a scrolling container are wrapped in repaint
/// boundaries so that they do not need to be repainted as the list scrolls.
/// If the children are easy to repaint (e.g., solid color blocks or a short
/// snippet of text), it might be more efficient to not add a repaint boundary
/// and simply repaint the children during scrolling.
///
/// Defaults to true.
final
bool
addRepaintBoundaries
;
@override
@override
Widget
build
(
BuildContext
context
,
int
index
)
{
Widget
build
(
BuildContext
context
,
int
index
)
{
assert
(
builder
!=
null
);
assert
(
builder
!=
null
);
...
@@ -153,7 +174,7 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -153,7 +174,7 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
final
Widget
child
=
builder
(
context
,
index
);
final
Widget
child
=
builder
(
context
,
index
);
if
(
child
==
null
)
if
(
child
==
null
)
return
null
;
return
null
;
return
new
RepaintBoundary
.
wrap
(
child
,
index
)
;
return
addRepaintBoundaries
?
new
RepaintBoundary
.
wrap
(
child
,
index
)
:
child
;
}
}
@override
@override
...
@@ -183,6 +204,9 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -183,6 +204,9 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
/// demand). For example, the body of a dialog box might fit both of these
/// demand). For example, the body of a dialog box might fit both of these
/// conditions.
/// conditions.
///
///
/// The widgets in the given [children] list are automatically wrapped in
/// [RepaintBoundary] widgets if [addRepaintBoundaries] is true (the default).
///
/// See also:
/// See also:
///
///
/// * [SliverChildBuilderDelegate], which is a delegate that uses a builder
/// * [SliverChildBuilderDelegate], which is a delegate that uses a builder
...
@@ -190,7 +214,13 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -190,7 +214,13 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
class
SliverChildListDelegate
extends
SliverChildDelegate
{
class
SliverChildListDelegate
extends
SliverChildDelegate
{
/// Creates a delegate that supplies children for slivers using the given
/// Creates a delegate that supplies children for slivers using the given
/// list.
/// list.
const
SliverChildListDelegate
(
this
.
children
,
{
this
.
addRepaintBoundaries
:
true
});
///
/// The [children] and [addRepaintBoundaries] arguments must not be null.
const
SliverChildListDelegate
(
this
.
children
,
{
this
.
addRepaintBoundaries
:
true
,
})
:
assert
(
children
!=
null
),
assert
(
addRepaintBoundaries
!=
null
);
/// Whether to wrap each child in a [RepaintBoundary].
/// Whether to wrap each child in a [RepaintBoundary].
///
///
...
@@ -815,3 +845,44 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
...
@@ -815,3 +845,44 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
@override
@override
RenderSliverFillRemaining
createRenderObject
(
BuildContext
context
)
=>
new
RenderSliverFillRemaining
();
RenderSliverFillRemaining
createRenderObject
(
BuildContext
context
)
=>
new
RenderSliverFillRemaining
();
}
}
/// 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
/// [SliverGrid] or [SliverList].
class
KeepAlive
extends
ParentDataWidget
<
SliverMultiBoxAdaptorWidget
>
{
/// Marks a child as needing to remain alive.
///
/// The [child] and [keepAlive] arguments must not be null.
KeepAlive
({
Key
key
,
@required
this
.
keepAlive
,
@required
Widget
child
,
})
:
assert
(
child
!=
null
),
assert
(
keepAlive
!=
null
),
super
(
key:
key
,
child:
child
);
/// Whether to keep the child alive.
///
/// If this is false, it is as if this widget was omitted.
final
bool
keepAlive
;
@override
void
applyParentData
(
RenderObject
renderObject
)
{
assert
(
renderObject
.
parentData
is
SliverMultiBoxAdaptorParentData
);
final
SliverMultiBoxAdaptorParentData
parentData
=
renderObject
.
parentData
;
if
(
parentData
.
keepAlive
!=
keepAlive
)
{
parentData
.
keepAlive
=
keepAlive
;
final
AbstractNode
targetParent
=
renderObject
.
parent
;
if
(
targetParent
is
RenderObject
)
targetParent
.
markNeedsLayout
();
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'keepAlive:
$keepAlive
'
);
}
}
packages/flutter/test/rendering/slivers_block_test.dart
View file @
52795630
...
@@ -244,4 +244,23 @@ void main() {
...
@@ -244,4 +244,23 @@ void main() {
expect
(
inner
.
geometry
.
scrollOffsetCorrection
,
isNull
);
expect
(
inner
.
geometry
.
scrollOffsetCorrection
,
isNull
);
});
});
test
(
'SliverMultiBoxAdaptorParentData.toString'
,
()
{
final
SliverMultiBoxAdaptorParentData
candidate
=
new
SliverMultiBoxAdaptorParentData
();
expect
(
candidate
.
keepAlive
,
isFalse
);
expect
(
candidate
.
index
,
isNull
);
expect
(
candidate
.
toString
(),
'index=null; layoutOffset=0.0'
);
candidate
.
keepAlive
=
null
;
expect
(
candidate
.
toString
(),
'index=null; layoutOffset=0.0'
);
candidate
.
keepAlive
=
true
;
expect
(
candidate
.
toString
(),
'index=null; keepAlive; layoutOffset=0.0'
);
candidate
.
keepAlive
=
false
;
expect
(
candidate
.
toString
(),
'index=null; layoutOffset=0.0'
);
candidate
.
index
=
0
;
expect
(
candidate
.
toString
(),
'index=0; layoutOffset=0.0'
);
candidate
.
index
=
1
;
expect
(
candidate
.
toString
(),
'index=1; layoutOffset=0.0'
);
candidate
.
index
=
-
1
;
expect
(
candidate
.
toString
(),
'index=-1; layoutOffset=0.0'
);
});
}
}
packages/flutter/test/widgets/keep_alive_test.dart
0 → 100644
View file @
52795630
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
show
Platform
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
class
Leaf
extends
StatefulWidget
{
Leaf
({
Key
key
,
this
.
index
,
this
.
child
})
:
super
(
key:
key
);
final
int
index
;
final
Widget
child
;
@override
_LeafState
createState
()
=>
new
_LeafState
();
}
class
_LeafState
extends
State
<
Leaf
>
{
bool
_keepAlive
=
false
;
void
setKeepAlive
(
bool
value
)
{
setState
(()
{
_keepAlive
=
value
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
KeepAlive
(
keepAlive:
_keepAlive
,
child:
widget
.
child
,
);
}
}
List
<
Widget
>
generateList
(
Widget
child
)
{
return
new
List
<
Widget
>.
generate
(
100
,
(
int
index
)
=>
new
Leaf
(
key:
new
GlobalObjectKey
<
_LeafState
>(
index
),
index:
index
,
child:
child
,
),
growable:
false
,
);
}
void
main
(
)
{
testWidgets
(
'KeepAlive with ListView with itemExtent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
ListView
(
addRepaintBoundaries:
false
,
itemExtent:
12.3
,
// about 50 widgets visible
children:
generateList
(
const
Placeholder
()),
));
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
300.0
));
// about 25 widgets' worth
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
true
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
300.0
));
// back to top
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
false
);
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
});
testWidgets
(
'KeepAlive with ListView without itemExtent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
ListView
(
addRepaintBoundaries:
false
,
children:
generateList
(
new
Container
(
height:
12.3
,
child:
const
Placeholder
())),
// about 50 widgets visible
));
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
300.0
));
// about 25 widgets' worth
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
true
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
300.0
));
// back to top
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
false
);
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
});
testWidgets
(
'KeepAlive with GridView'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
GridView
.
count
(
addRepaintBoundaries:
false
,
crossAxisCount:
2
,
childAspectRatio:
400.0
/
24.6
,
// about 50 widgets visible
children:
generateList
(
new
Container
(
child:
const
Placeholder
())),
));
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
await
tester
.
drag
(
find
.
byType
(
GridView
),
const
Offset
(
0.0
,
-
300.0
));
// about 25 widgets' worth
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
true
);
await
tester
.
drag
(
find
.
byType
(
GridView
),
const
Offset
(
0.0
,
300.0
));
// back to top
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
const
GlobalObjectKey
<
_LeafState
>(
60
).
currentState
.
setKeepAlive
(
false
);
await
tester
.
pump
();
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
3
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
30
)),
findsOneWidget
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
59
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
60
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
61
)),
findsNothing
);
expect
(
find
.
byKey
(
const
GlobalObjectKey
<
_LeafState
>(
90
)),
findsNothing
);
});
testWidgets
(
'KeepAlive render tree description'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
ListView
(
addRepaintBoundaries:
false
,
itemExtent:
400.0
,
// 2 visible children
children:
generateList
(
const
Placeholder
()),
));
// The important lines below are the ones marked with "<----"
expect
(
tester
.
binding
.
renderView
.
toStringDeep
(),
equalsIgnoringHashCodes
(
'RenderView#00000
\n
'
' │ debug mode enabled -
${Platform.operatingSystem}
\n
'
' │ window size: Size(2400.0, 1800.0) (in physical pixels)
\n
'
' │ device pixel ratio: 3.0 (physical pixels per logical pixel)
\n
'
' │ configuration: Size(800.0, 600.0) at 3.0x (in logical pixels)
\n
'
' │
\n
'
' └─child: RenderRepaintBoundary#00000
\n
'
' │ creator: RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ metrics: 0.0% useful (1 bad vs 0 good)
\n
'
' │ diagnosis: insufficient data to draw conclusion (less than five
\n
'
' │ repaints)
\n
'
' │
\n
'
' └─child: RenderCustomPaint#00000
\n
'
' │ creator: CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │
\n
'
' └─child: RenderRepaintBoundary#00000
\n
'
' │ creator: RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ metrics: 0.0% useful (1 bad vs 0 good)
\n
'
' │ diagnosis: insufficient data to draw conclusion (less than five
\n
'
' │ repaints)
\n
'
' │
\n
'
' └─child: RenderSemanticsGestureHandler#00000
\n
'
' │ creator: _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ semantic boundary
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ gestures: horizontal scroll, vertical scroll
\n
'
' │
\n
'
' └─child: RenderPointerListener#00000
\n
'
' │ creator: Listener ← _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ behavior: opaque
\n
'
' │ listeners: down
\n
'
' │
\n
'
' └─child: RenderIgnorePointer#00000
\n
'
' │ creator: IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ ignoring: false
\n
'
' │ ignoringSemantics: implicitly false
\n
'
' │
\n
'
' └─child: RenderViewport#00000
\n
'
' │ creator: Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ⋯
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ AxisDirection.down
\n
'
' │ offset: ScrollPositionWithSingleContext#00000(offset: 0.0, range:
\n
'
' │ 0.0..39400.0, viewport: 600.0, ScrollableState,
\n
'
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,
\n
'
' │ IdleScrollActivity#00000, ScrollDirection.idle)
\n
'
' │ anchor: 0.0
\n
'
' │
\n
'
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1
\n
'
' │ creator: SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← ⋯
\n
'
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)
\n
'
' │ constraints: SliverConstraints(AxisDirection.down,
\n
'
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:
\n
'
' │ 0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,
\n
'
' │ viewportMainAxisExtent: 600.0)
\n
'
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:
\n
'
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true, )
\n
'
' │ currently live children: 0 to 1
\n
'
' │
\n
'
' ├─child with index 0: RenderLimitedBox#00000
\n
'
' │ │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ │ ←
\n
'
' │ │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ │ ← RepaintBoundary ← ⋯
\n
'
' │ │ parentData: index=0; layoutOffset=0.0
\n
'
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ │ size: Size(800.0, 400.0)
\n
'
' │ │ maxWidth: 400.0
\n
'
' │ │ maxHeight: 400.0
\n
'
' │ │
\n
'
' │ └─child: RenderCustomPaint#00000
\n
'
' │ creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← ⋯
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ size: Size(800.0, 400.0)
\n
'
' │
\n
'
' └─child with index 1: RenderLimitedBox#00000
\n
'
// <----- no dashed line starts here
' │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← ⋯
\n
'
' │ parentData: index=1; layoutOffset=400.0
\n
'
' │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ size: Size(800.0, 400.0)
\n
'
' │ maxWidth: 400.0
\n
'
' │ maxHeight: 400.0
\n
'
' │
\n
'
' └─child: RenderCustomPaint#00000
\n
'
' creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ←
\n
'
' RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ← ⋯
\n
'
' parentData: <none> (can use size)
\n
'
' constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' size: Size(800.0, 400.0)
\n
'
''
// TODO(ianh): remove blank line
));
const
GlobalObjectKey
<
_LeafState
>(
0
).
currentState
.
setKeepAlive
(
true
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
1000.0
));
await
tester
.
pump
();
const
GlobalObjectKey
<
_LeafState
>(
3
).
currentState
.
setKeepAlive
(
true
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
1000.0
));
await
tester
.
pump
();
expect
(
tester
.
binding
.
renderView
.
toStringDeep
(),
equalsIgnoringHashCodes
(
'RenderView#00000
\n
'
' │ debug mode enabled -
${Platform.operatingSystem}
\n
'
' │ window size: Size(2400.0, 1800.0) (in physical pixels)
\n
'
' │ device pixel ratio: 3.0 (physical pixels per logical pixel)
\n
'
' │ configuration: Size(800.0, 600.0) at 3.0x (in logical pixels)
\n
'
' │
\n
'
' └─child: RenderRepaintBoundary#00000
\n
'
' │ creator: RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ metrics: 0.0% useful (1 bad vs 0 good)
\n
'
' │ diagnosis: insufficient data to draw conclusion (less than five
\n
'
' │ repaints)
\n
'
' │
\n
'
' └─child: RenderCustomPaint#00000
\n
'
' │ creator: CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │
\n
'
' └─child: RenderRepaintBoundary#00000
\n
'
' │ creator: RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ metrics: 0.0% useful (1 bad vs 0 good)
\n
'
' │ diagnosis: insufficient data to draw conclusion (less than five
\n
'
' │ repaints)
\n
'
' │
\n
'
' └─child: RenderSemanticsGestureHandler#00000
\n
'
' │ creator: _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ semantic boundary
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ gestures: horizontal scroll, vertical scroll
\n
'
' │
\n
'
' └─child: RenderPointerListener#00000
\n
'
' │ creator: Listener ← _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ behavior: opaque
\n
'
' │ listeners: down
\n
'
' │
\n
'
' └─child: RenderIgnorePointer#00000
\n
'
' │ creator: IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _GestureSemantics ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ListView ← [root]
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ ignoring: false
\n
'
' │ ignoringSemantics: implicitly false
\n
'
' │
\n
'
' └─child: RenderViewport#00000
\n
'
' │ creator: Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← Scrollable ← ⋯
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ layer: OffsetLayer#00000
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ AxisDirection.down
\n
'
' │ offset: ScrollPositionWithSingleContext#00000(offset: 2000.0,
\n
'
' │ range: 0.0..39400.0, viewport: 600.0, ScrollableState,
\n
'
' │ AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,
\n
'
' │ IdleScrollActivity#00000, ScrollDirection.idle)
\n
'
' │ anchor: 0.0
\n
'
' │
\n
'
' └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1
\n
'
' │ creator: SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← CustomPaint ← RepaintBoundary ←
\n
'
' │ NotificationListener<ScrollNotification> ←
\n
'
' │ GlowingOverscrollIndicator ← ⋯
\n
'
' │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)
\n
'
' │ constraints: SliverConstraints(AxisDirection.down,
\n
'
' │ GrowthDirection.forward, ScrollDirection.idle, scrollOffset:
\n
'
' │ 2000.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,
\n
'
' │ viewportMainAxisExtent: 600.0)
\n
'
' │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:
\n
'
' │ 600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true, )
\n
'
' │ currently live children: 5 to 6
\n
'
' │
\n
'
' ├─child with index 5: RenderLimitedBox#00000
\n
'
// <----- this is index 5, not 0
' │ │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ │ ←
\n
'
' │ │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ │ ← RepaintBoundary ← ⋯
\n
'
' │ │ parentData: index=5; layoutOffset=2000.0
\n
'
' │ │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ │ size: Size(800.0, 400.0)
\n
'
' │ │ maxWidth: 400.0
\n
'
' │ │ maxHeight: 400.0
\n
'
' │ │
\n
'
' │ └─child: RenderCustomPaint#00000
\n
'
' │ creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← ⋯
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ size: Size(800.0, 400.0)
\n
'
' │
\n
'
' ├─child with index 6: RenderLimitedBox#00000
\n
'
' ╎ │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
// <----- the line starts becoming dashed here
' ╎ │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' ╎ │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' ╎ │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ╎ │ ←
\n
'
' ╎ │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ╎ │ ← RepaintBoundary ← ⋯
\n
'
' ╎ │ parentData: index=6; layoutOffset=2400.0
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' ╎ │ size: Size(800.0, 400.0)
\n
'
' ╎ │ maxWidth: 400.0
\n
'
' ╎ │ maxHeight: 400.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderCustomPaint#00000
\n
'
' ╎ creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' ╎ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' ╎ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' ╎ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ╎ ←
\n
'
' ╎ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ╎ ← ⋯
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' ╎ size: Size(800.0, 400.0)
\n
'
' ╎
\n
'
' ├─child with index 0 (kept alive offstage): RenderLimitedBox#00000
\n
'
// <----- this one is index 0 and is marked as being offstage
' ╎ │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' ╎ │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' ╎ │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' ╎ │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ╎ │ ←
\n
'
' ╎ │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ╎ │ ← RepaintBoundary ← ⋯
\n
'
' ╎ │ parentData: index=0; keepAlive; layoutOffset=0.0
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' ╎ │ size: Size(800.0, 400.0)
\n
'
' ╎ │ maxWidth: 400.0
\n
'
' ╎ │ maxHeight: 400.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderCustomPaint#00000
\n
'
' ╎ creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' ╎ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' ╎ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' ╎ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ╎ ←
\n
'
' ╎ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ╎ ← ⋯
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' ╎ size: Size(800.0, 400.0)
\n
'
' ╎
\n
'
// <----- dashed line ends here
' └─child with index 3 (kept alive offstage): RenderLimitedBox#00000
\n
'
' │ creator: LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' │ Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' │ SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' │ IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' │ ←
\n
'
' │ RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' │ ← RepaintBoundary ← ⋯
\n
'
' │ parentData: index=3; keepAlive; layoutOffset=1200.0
\n
'
' │ constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' │ size: Size(800.0, 400.0)
\n
'
' │ maxWidth: 400.0
\n
'
' │ maxHeight: 400.0
\n
'
' │
\n
'
' └─child: RenderCustomPaint#00000
\n
'
' creator: CustomPaint ← LimitedBox ← Placeholder ← KeepAlive ←
\n
'
' Leaf-[GlobalObjectKey<_LeafState> int#00000] ←
\n
'
' SliverFixedExtentList ← Viewport ← _ScrollableScope ←
\n
'
' IgnorePointer-[GlobalKey#00000] ← Listener ← _GestureSemantics
\n
'
' ←
\n
'
' RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#00000]
\n
'
' ← ⋯
\n
'
' parentData: <none> (can use size)
\n
'
' constraints: BoxConstraints(w=800.0, h=400.0)
\n
'
' size: Size(800.0, 400.0)
\n
'
''
// TODO(ianh): remove blank line
));
});
}
packages/flutter/test/widgets/list_view_viewporting_test.dart
View file @
52795630
...
@@ -295,7 +295,7 @@ void main() {
...
@@ -295,7 +295,7 @@ void main() {
' │ maxPaintExtent: 300.0, )
\n
'
' │ maxPaintExtent: 300.0, )
\n
'
' │ currently live children: 0 to 2
\n
'
' │ currently live children: 0 to 2
\n
'
' │
\n
'
' │
\n
'
' ├─child
1
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' ├─child
with index 0
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' │ │ creator: RepaintBoundary-[<0>] ← SliverList ← Viewport ←
\n
'
' │ │ creator: RepaintBoundary-[<0>] ← SliverList ← Viewport ←
\n
'
' │ │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ │ _GestureSemantics ←
\n
'
' │ │ _GestureSemantics ←
\n
'
...
@@ -347,7 +347,7 @@ void main() {
...
@@ -347,7 +347,7 @@ void main() {
' │ size: Size(800.0, 100.0)
\n
'
' │ size: Size(800.0, 100.0)
\n
'
' │ additionalConstraints: BoxConstraints(biggest)
\n
'
' │ additionalConstraints: BoxConstraints(biggest)
\n
'
' │
\n
'
' │
\n
'
' ├─child
2
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' ├─child
with index 1
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' │ │ creator: RepaintBoundary-[<1>] ← SliverList ← Viewport ←
\n
'
' │ │ creator: RepaintBoundary-[<1>] ← SliverList ← Viewport ←
\n
'
' │ │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ │ _GestureSemantics ←
\n
'
' │ │ _GestureSemantics ←
\n
'
...
@@ -399,7 +399,7 @@ void main() {
...
@@ -399,7 +399,7 @@ void main() {
' │ size: Size(800.0, 100.0)
\n
'
' │ size: Size(800.0, 100.0)
\n
'
' │ additionalConstraints: BoxConstraints(biggest)
\n
'
' │ additionalConstraints: BoxConstraints(biggest)
\n
'
' │
\n
'
' │
\n
'
' └─child
3
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' └─child
with index 2
: RenderRepaintBoundary#00000 relayoutBoundary=up2
\n
'
' │ creator: RepaintBoundary-[<2>] ← SliverList ← Viewport ←
\n
'
' │ creator: RepaintBoundary-[<2>] ← SliverList ← Viewport ←
\n
'
' │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _GestureSemantics ←
\n
'
' │ _GestureSemantics ←
\n
'
...
...
packages/flutter/test/widgets/sliver_fill_viewport_test.dart
View file @
52795630
...
@@ -80,7 +80,7 @@ void main() {
...
@@ -80,7 +80,7 @@ void main() {
' │ 600.0, maxPaintExtent: 12000.0, hasVisualOverflow: true, )
\n
'
' │ 600.0, maxPaintExtent: 12000.0, hasVisualOverflow: true, )
\n
'
' │ currently live children: 0 to 0
\n
'
' │ currently live children: 0 to 0
\n
'
' │
\n
'
' │
\n
'
' └─child
1
: RenderRepaintBoundary#00000
\n
'
' └─child
with index 0
: RenderRepaintBoundary#00000
\n
'
' │ creator: RepaintBoundary-[<0>] ← SliverFillViewport ← Viewport ←
\n
'
' │ creator: RepaintBoundary-[<0>] ← SliverFillViewport ← Viewport ←
\n
'
' │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _ScrollableScope ← IgnorePointer-[GlobalKey#00000] ← Listener ←
\n
'
' │ _GestureSemantics ←
\n
'
' │ _GestureSemantics ←
\n
'
...
...
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