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
38808d9f
Unverified
Commit
38808d9f
authored
May 03, 2019
by
chunhtai
Committed by
GitHub
May 03, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reland fix 25807 implement move for sliver multibox widget (#31978)
parent
39d660be
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1125 additions
and
67 deletions
+1125
-67
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+23
-15
sliver_multi_box_adaptor.dart
...s/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
+91
-11
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+7
-0
sliver.dart
packages/flutter/lib/src/widgets/sliver.dart
+157
-14
tabs_test.dart
packages/flutter/test/material/tabs_test.dart
+52
-1
box_sliver_mismatch_test.dart
packages/flutter/test/widgets/box_sliver_mismatch_test.dart
+5
-5
framework_test.dart
packages/flutter/test/widgets/framework_test.dart
+35
-0
slivers_appbar_floating_pinned_test.dart
...ter/test/widgets/slivers_appbar_floating_pinned_test.dart
+4
-4
slivers_appbar_floating_test.dart
...es/flutter/test/widgets/slivers_appbar_floating_test.dart
+2
-2
slivers_appbar_pinned_test.dart
...ages/flutter/test/widgets/slivers_appbar_pinned_test.dart
+2
-2
slivers_appbar_scrolling_test.dart
...s/flutter/test/widgets/slivers_appbar_scrolling_test.dart
+33
-1
slivers_block_test.dart
packages/flutter/test/widgets/slivers_block_test.dart
+66
-11
slivers_keepalive_test.dart
packages/flutter/test/widgets/slivers_keepalive_test.dart
+646
-0
slivers_test.dart
packages/flutter/test/widgets/slivers_test.dart
+2
-1
No files found.
packages/flutter/lib/src/material/tabs.dart
View file @
38808d9f
...
@@ -1125,6 +1125,7 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1125,6 +1125,7 @@ class _TabBarViewState extends State<TabBarView> {
TabController
_controller
;
TabController
_controller
;
PageController
_pageController
;
PageController
_pageController
;
List
<
Widget
>
_children
;
List
<
Widget
>
_children
;
List
<
Widget
>
_childrenWithKey
;
int
_currentIndex
;
int
_currentIndex
;
int
_warpUnderwayCount
=
0
;
int
_warpUnderwayCount
=
0
;
...
@@ -1156,7 +1157,7 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1156,7 +1157,7 @@ class _TabBarViewState extends State<TabBarView> {
@override
@override
void
initState
()
{
void
initState
()
{
super
.
initState
();
super
.
initState
();
_
children
=
widget
.
children
;
_
updateChildren
()
;
}
}
@override
@override
...
@@ -1173,7 +1174,7 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1173,7 +1174,7 @@ class _TabBarViewState extends State<TabBarView> {
if
(
widget
.
controller
!=
oldWidget
.
controller
)
if
(
widget
.
controller
!=
oldWidget
.
controller
)
_updateTabController
();
_updateTabController
();
if
(
widget
.
children
!=
oldWidget
.
children
&&
_warpUnderwayCount
==
0
)
if
(
widget
.
children
!=
oldWidget
.
children
&&
_warpUnderwayCount
==
0
)
_
children
=
widget
.
children
;
_
updateChildren
()
;
}
}
@override
@override
...
@@ -1184,6 +1185,11 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1184,6 +1185,11 @@ class _TabBarViewState extends State<TabBarView> {
super
.
dispose
();
super
.
dispose
();
}
}
void
_updateChildren
()
{
_children
=
widget
.
children
;
_childrenWithKey
=
KeyedSubtree
.
ensureUniqueKeysForList
(
widget
.
children
);
}
void
_handleTabControllerAnimationTick
()
{
void
_handleTabControllerAnimationTick
()
{
if
(
_warpUnderwayCount
>
0
||
!
_controller
.
indexIsChanging
)
if
(
_warpUnderwayCount
>
0
||
!
_controller
.
indexIsChanging
)
return
;
// This widget is driving the controller's animation.
return
;
// This widget is driving the controller's animation.
...
@@ -1206,28 +1212,30 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1206,28 +1212,30 @@ class _TabBarViewState extends State<TabBarView> {
return
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
return
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
assert
((
_currentIndex
-
previousIndex
).
abs
()
>
1
);
assert
((
_currentIndex
-
previousIndex
).
abs
()
>
1
);
int
initialPage
;
final
int
initialPage
=
_currentIndex
>
previousIndex
?
_currentIndex
-
1
:
_currentIndex
+
1
;
final
List
<
Widget
>
originalChildren
=
_childrenWithKey
;
setState
(()
{
setState
(()
{
_warpUnderwayCount
+=
1
;
_warpUnderwayCount
+=
1
;
_children
=
List
<
Widget
>.
from
(
widget
.
children
,
growable:
false
);
if
(
_currentIndex
>
previousIndex
)
{
_children
[
_currentIndex
-
1
]
=
_children
[
previousIndex
];
initialPage
=
_currentIndex
-
1
;
}
else
{
_children
[
_currentIndex
+
1
]
=
_children
[
previousIndex
];
initialPage
=
_currentIndex
+
1
;
}
});
_childrenWithKey
=
List
<
Widget
>.
from
(
_childrenWithKey
,
growable:
false
);
final
Widget
temp
=
_childrenWithKey
[
initialPage
];
_childrenWithKey
[
initialPage
]
=
_childrenWithKey
[
previousIndex
];
_childrenWithKey
[
previousIndex
]
=
temp
;
});
_pageController
.
jumpToPage
(
initialPage
);
_pageController
.
jumpToPage
(
initialPage
);
await
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
await
_pageController
.
animateToPage
(
_currentIndex
,
duration:
kTabScrollDuration
,
curve:
Curves
.
ease
);
if
(!
mounted
)
if
(!
mounted
)
return
Future
<
void
>.
value
();
return
Future
<
void
>.
value
();
setState
(()
{
setState
(()
{
_warpUnderwayCount
-=
1
;
_warpUnderwayCount
-=
1
;
_children
=
widget
.
children
;
if
(
widget
.
children
!=
_children
)
{
_updateChildren
();
}
else
{
_childrenWithKey
=
originalChildren
;
}
});
});
}
}
...
@@ -1272,7 +1280,7 @@ class _TabBarViewState extends State<TabBarView> {
...
@@ -1272,7 +1280,7 @@ class _TabBarViewState extends State<TabBarView> {
dragStartBehavior:
widget
.
dragStartBehavior
,
dragStartBehavior:
widget
.
dragStartBehavior
,
controller:
_pageController
,
controller:
_pageController
,
physics:
widget
.
physics
==
null
?
_kTabBarViewPhysics
:
_kTabBarViewPhysics
.
applyTo
(
widget
.
physics
),
physics:
widget
.
physics
==
null
?
_kTabBarViewPhysics
:
_kTabBarViewPhysics
.
applyTo
(
widget
.
physics
),
children:
_children
,
children:
_children
WithKey
,
),
),
);
);
}
}
...
...
packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart
View file @
38808d9f
...
@@ -85,7 +85,8 @@ abstract class RenderSliverBoxChildManager {
...
@@ -85,7 +85,8 @@ abstract class RenderSliverBoxChildManager {
/// list).
/// list).
int
get
childCount
;
int
get
childCount
;
/// Called during [RenderSliverMultiBoxAdaptor.adoptChild].
/// Called during [RenderSliverMultiBoxAdaptor.adoptChild] or
/// [RenderSliverMultiBoxAdaptor.move].
///
///
/// Subclasses must ensure that the [SliverMultiBoxAdaptorParentData.index]
/// Subclasses must ensure that the [SliverMultiBoxAdaptorParentData.index]
/// field of the child's [RenderObject.parentData] accurately reflects the
/// field of the child's [RenderObject.parentData] accurately reflects the
...
@@ -193,7 +194,12 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -193,7 +194,12 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
RenderSliverMultiBoxAdaptor
({
RenderSliverMultiBoxAdaptor
({
@required
RenderSliverBoxChildManager
childManager
,
@required
RenderSliverBoxChildManager
childManager
,
})
:
assert
(
childManager
!=
null
),
})
:
assert
(
childManager
!=
null
),
_childManager
=
childManager
;
_childManager
=
childManager
{
assert
(()
{
_debugDanglingKeepAlives
=
<
RenderBox
>[];
return
true
;
}());
}
@override
@override
void
setupParentData
(
RenderObject
child
)
{
void
setupParentData
(
RenderObject
child
)
{
...
@@ -214,6 +220,27 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -214,6 +220,27 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
/// The nodes being kept alive despite not being visible.
/// The nodes being kept alive despite not being visible.
final
Map
<
int
,
RenderBox
>
_keepAliveBucket
=
<
int
,
RenderBox
>{};
final
Map
<
int
,
RenderBox
>
_keepAliveBucket
=
<
int
,
RenderBox
>{};
List
<
RenderBox
>
_debugDanglingKeepAlives
;
/// Indicates whether integrity check is enabled.
///
/// Setting this property to true will immediately perform an integrity check.
///
/// The integrity check consists of:
///
/// 1. Verify that the children index in childList is in ascending order.
/// 2. Verify that there is no dangling keepalive child as the result of [move].
bool
get
debugChildIntegrityEnabled
=>
_debugChildIntegrityEnabled
;
bool
_debugChildIntegrityEnabled
=
true
;
set
debugChildIntegrityEnabled
(
bool
enabled
)
{
assert
(
enabled
!=
null
);
assert
(()
{
_debugChildIntegrityEnabled
=
enabled
;
return
_debugVerifyChildOrder
()
&&
(!
_debugChildIntegrityEnabled
||
_debugDanglingKeepAlives
.
isEmpty
);
}());
}
@override
@override
void
adoptChild
(
RenderObject
child
)
{
void
adoptChild
(
RenderObject
child
)
{
super
.
adoptChild
(
child
);
super
.
adoptChild
(
child
);
...
@@ -224,21 +251,70 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -224,21 +251,70 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
bool
_debugAssertChildListLocked
()
=>
childManager
.
debugAssertChildListLocked
();
bool
_debugAssertChildListLocked
()
=>
childManager
.
debugAssertChildListLocked
();
/// Verify that the child list index is in strictly increasing order.
///
/// This has no effect in release builds.
bool
_debugVerifyChildOrder
(){
if
(
_debugChildIntegrityEnabled
)
{
RenderBox
child
=
firstChild
;
int
index
;
while
(
child
!=
null
)
{
index
=
indexOf
(
child
);
child
=
childAfter
(
child
);
assert
(
child
==
null
||
indexOf
(
child
)
>
index
);
}
}
return
true
;
}
@override
@override
void
insert
(
RenderBox
child
,
{
RenderBox
after
})
{
void
insert
(
RenderBox
child
,
{
RenderBox
after
})
{
assert
(!
_keepAliveBucket
.
containsValue
(
child
));
assert
(!
_keepAliveBucket
.
containsValue
(
child
));
super
.
insert
(
child
,
after:
after
);
super
.
insert
(
child
,
after:
after
);
assert
(
firstChild
!=
null
);
assert
(
firstChild
!=
null
);
assert
(()
{
assert
(
_debugVerifyChildOrder
());
int
index
=
indexOf
(
firstChild
);
}
RenderBox
child
=
childAfter
(
firstChild
);
while
(
child
!=
null
)
{
@override
assert
(
indexOf
(
child
)
>
index
);
void
move
(
RenderBox
child
,
{
RenderBox
after
})
{
index
=
indexOf
(
child
);
// There are two scenarios:
child
=
childAfter
(
child
);
//
// 1. The child is not keptAlive.
// The child is in the childList maintained by ContainerRenderObjectMixin.
// We can call super.move and update parentData with the new slot.
//
// 2. The child is keptAlive.
// In this case, the child is no longer in the childList but might be stored in
// [_keepAliveBucket]. We need to update the location of the child in the bucket.
final
SliverMultiBoxAdaptorParentData
childParentData
=
child
.
parentData
;
if
(!
childParentData
.
keptAlive
)
{
super
.
move
(
child
,
after:
after
);
childManager
.
didAdoptChild
(
child
);
// updates the slot in the parentData
// Its slot may change even if super.move does not change the position.
// In this case, we still want to mark as needs layout.
markNeedsLayout
();
}
else
{
// If the child in the bucket is not current child, that means someone has
// already moved and replaced current child, and we cannot remove this child.
if
(
_keepAliveBucket
[
childParentData
.
index
]
==
child
)
{
_keepAliveBucket
.
remove
(
childParentData
.
index
);
}
}
assert
(()
{
_debugDanglingKeepAlives
.
remove
(
child
);
return
true
;
}());
// Update the slot and reinsert back to _keepAliveBucket in the new slot.
childManager
.
didAdoptChild
(
child
);
// If there is an existing child in the new slot, that mean that child will
// be moved to other index. In other cases, the existing child should have been
// removed by updateChild. Thus, it is ok to overwrite it.
assert
(()
{
if
(
_keepAliveBucket
.
containsKey
(
childParentData
.
index
))
_debugDanglingKeepAlives
.
add
(
_keepAliveBucket
[
childParentData
.
index
]);
return
true
;
return
true
;
}());
}());
_keepAliveBucket
[
childParentData
.
index
]
=
child
;
}
}
}
@override
@override
...
@@ -249,6 +325,10 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
...
@@ -249,6 +325,10 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
return
;
return
;
}
}
assert
(
_keepAliveBucket
[
childParentData
.
index
]
==
child
);
assert
(
_keepAliveBucket
[
childParentData
.
index
]
==
child
);
assert
(()
{
_debugDanglingKeepAlives
.
remove
(
child
);
return
true
;
}());
_keepAliveBucket
.
remove
(
childParentData
.
index
);
_keepAliveBucket
.
remove
(
childParentData
.
index
);
dropChild
(
child
);
dropChild
(
child
);
}
}
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
38808d9f
...
@@ -149,6 +149,13 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
...
@@ -149,6 +149,13 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
assert
(()
{
assert
(()
{
assert
(
parent
!=
null
);
assert
(
parent
!=
null
);
if
(
_debugReservations
.
containsKey
(
this
)
&&
_debugReservations
[
this
]
!=
parent
)
{
if
(
_debugReservations
.
containsKey
(
this
)
&&
_debugReservations
[
this
]
!=
parent
)
{
// Reserving a new parent while the old parent is not attached is ok.
// This can happen when a renderObject detaches and re-attaches to rendering
// tree multiple times.
if
(
_debugReservations
[
this
].
renderObject
?.
attached
==
false
)
{
_debugReservations
[
this
]
=
parent
;
return
true
;
}
// It's possible for an element to get built multiple times in one
// It's possible for an element to get built multiple times in one
// frame, in which case it'll reserve the same child's key multiple
// frame, in which case it'll reserve the same child's key multiple
// times. We catch multiple children of one widget having the same key
// times. We catch multiple children of one widget having the same key
...
...
packages/flutter/lib/src/widgets/sliver.dart
View file @
38808d9f
...
@@ -174,6 +174,13 @@ abstract class SliverChildDelegate {
...
@@ -174,6 +174,13 @@ abstract class SliverChildDelegate {
/// away.
/// away.
bool
shouldRebuild
(
covariant
SliverChildDelegate
oldDelegate
);
bool
shouldRebuild
(
covariant
SliverChildDelegate
oldDelegate
);
/// Find index of child element with associated key.
///
/// This will be called during [performRebuild] in [SliverMultiBoxAdaptorElement]
/// to check if a child has moved to a different position. It should return the
/// index of the child element with associated key, null if not found.
int
findIndexByKey
(
Key
key
)
=>
null
;
@override
@override
String
toString
()
{
String
toString
()
{
final
List
<
String
>
description
=
<
String
>[];
final
List
<
String
>
description
=
<
String
>[];
...
@@ -195,6 +202,12 @@ abstract class SliverChildDelegate {
...
@@ -195,6 +202,12 @@ abstract class SliverChildDelegate {
}
}
}
}
class
_SaltedValueKey
extends
ValueKey
<
Key
>{
const
_SaltedValueKey
(
Key
key
):
assert
(
key
!=
null
),
super
(
key
);
}
typedef
ChildIndexGetter
=
int
Function
(
Key
key
);
/// A delegate that supplies children for slivers using a builder callback.
/// A delegate that supplies children for slivers using a builder callback.
///
///
/// Many slivers lazily construct their box children to avoid creating more
/// Many slivers lazily construct their box children to avoid creating more
...
@@ -306,8 +319,14 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -306,8 +319,14 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
/// The [builder], [addAutomaticKeepAlives], [addRepaintBoundaries],
/// The [builder], [addAutomaticKeepAlives], [addRepaintBoundaries],
/// [addSemanticIndexes], and [semanticIndexCallback] arguments must not be
/// [addSemanticIndexes], and [semanticIndexCallback] arguments must not be
/// null.
/// null.
///
/// If the order in which [builder] returns children ever changes, consider
/// providing a [findChildIndex]. This allows the delegate to find the new index
/// for a child that was previously located at a different index to attach the
/// existing state to the [Widget] at its new location.
const
SliverChildBuilderDelegate
(
const
SliverChildBuilderDelegate
(
this
.
builder
,
{
this
.
builder
,
{
this
.
findChildIndexCallback
,
this
.
childCount
,
this
.
childCount
,
this
.
addAutomaticKeepAlives
=
true
,
this
.
addAutomaticKeepAlives
=
true
,
this
.
addRepaintBoundaries
=
true
,
this
.
addRepaintBoundaries
=
true
,
...
@@ -388,6 +407,31 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -388,6 +407,31 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
/// Defaults to providing an index for each widget.
/// Defaults to providing an index for each widget.
final
SemanticIndexCallback
semanticIndexCallback
;
final
SemanticIndexCallback
semanticIndexCallback
;
/// Called to find the new index of a child based on its key in case of reordering.
///
/// If not provided, a child widget may not map to its existing [RenderObject]
/// when the order in which children are returned from [builder] changes.
/// This may result in state-loss.
///
/// This callback should take an input [Key], and It should return the
/// index of the child element with associated key, null if not found.
final
ChildIndexGetter
findChildIndexCallback
;
@override
int
findIndexByKey
(
Key
key
)
{
if
(
findChildIndexCallback
==
null
)
return
null
;
assert
(
key
!=
null
);
Key
childKey
;
if
(
key
is
_SaltedValueKey
)
{
final
_SaltedValueKey
saltedValueKey
=
key
;
childKey
=
saltedValueKey
.
value
;
}
else
{
childKey
=
key
;
}
return
findChildIndexCallback
(
childKey
);
}
@override
@override
Widget
build
(
BuildContext
context
,
int
index
)
{
Widget
build
(
BuildContext
context
,
int
index
)
{
assert
(
builder
!=
null
);
assert
(
builder
!=
null
);
...
@@ -401,8 +445,9 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -401,8 +445,9 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
}
}
if
(
child
==
null
)
if
(
child
==
null
)
return
null
;
return
null
;
final
Key
key
=
child
.
key
!=
null
?
_SaltedValueKey
(
child
.
key
)
:
null
;
if
(
addRepaintBoundaries
)
if
(
addRepaintBoundaries
)
child
=
RepaintBoundary
.
wrap
(
child
,
index
);
child
=
RepaintBoundary
(
child:
child
);
if
(
addSemanticIndexes
)
{
if
(
addSemanticIndexes
)
{
final
int
semanticIndex
=
semanticIndexCallback
(
child
,
index
);
final
int
semanticIndex
=
semanticIndexCallback
(
child
,
index
);
if
(
semanticIndex
!=
null
)
if
(
semanticIndex
!=
null
)
...
@@ -410,7 +455,7 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
...
@@ -410,7 +455,7 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
}
}
if
(
addAutomaticKeepAlives
)
if
(
addAutomaticKeepAlives
)
child
=
AutomaticKeepAlive
(
child:
child
);
child
=
AutomaticKeepAlive
(
child:
child
);
return
child
;
return
KeyedSubtree
(
child:
child
,
key:
key
)
;
}
}
@override
@override
...
@@ -478,7 +523,10 @@ class SliverChildListDelegate extends SliverChildDelegate {
...
@@ -478,7 +523,10 @@ class SliverChildListDelegate extends SliverChildDelegate {
/// The [children], [addAutomaticKeepAlives], [addRepaintBoundaries],
/// The [children], [addAutomaticKeepAlives], [addRepaintBoundaries],
/// [addSemanticIndexes], and [semanticIndexCallback] arguments must not be
/// [addSemanticIndexes], and [semanticIndexCallback] arguments must not be
/// null.
/// null.
const
SliverChildListDelegate
(
///
/// If the order of children` never changes, consider using the constant
/// [SliverChildListDelegate.fixed] constructor.
SliverChildListDelegate
(
this
.
children
,
{
this
.
children
,
{
this
.
addAutomaticKeepAlives
=
true
,
this
.
addAutomaticKeepAlives
=
true
,
this
.
addRepaintBoundaries
=
true
,
this
.
addRepaintBoundaries
=
true
,
...
@@ -489,7 +537,31 @@ class SliverChildListDelegate extends SliverChildDelegate {
...
@@ -489,7 +537,31 @@ class SliverChildListDelegate extends SliverChildDelegate {
assert
(
addAutomaticKeepAlives
!=
null
),
assert
(
addAutomaticKeepAlives
!=
null
),
assert
(
addRepaintBoundaries
!=
null
),
assert
(
addRepaintBoundaries
!=
null
),
assert
(
addSemanticIndexes
!=
null
),
assert
(
addSemanticIndexes
!=
null
),
assert
(
semanticIndexCallback
!=
null
);
assert
(
semanticIndexCallback
!=
null
),
_keyToIndex
=
<
Key
,
int
>{
null
:
0
};
/// Creates a constant version of the delegate that supplies children for
/// slivers using the given list.
///
/// If the order of the children will change, consider using the regular
/// [SliverChildListDelegate] constructor.
///
/// The [children], [addAutomaticKeepAlives], [addRepaintBoundaries],
/// [addSemanticIndexes], and [semanticIndexCallback] arguments must not be
/// null.
const
SliverChildListDelegate
.
fixed
(
this
.
children
,
{
this
.
addAutomaticKeepAlives
=
true
,
this
.
addRepaintBoundaries
=
true
,
this
.
addSemanticIndexes
=
true
,
this
.
semanticIndexCallback
=
_kDefaultSemanticIndexCallback
,
this
.
semanticIndexOffset
=
0
,
})
:
assert
(
children
!=
null
),
assert
(
addAutomaticKeepAlives
!=
null
),
assert
(
addRepaintBoundaries
!=
null
),
assert
(
addSemanticIndexes
!=
null
),
assert
(
semanticIndexCallback
!=
null
),
_keyToIndex
=
null
;
/// Whether to wrap each child in an [AutomaticKeepAlive].
/// Whether to wrap each child in an [AutomaticKeepAlive].
///
///
...
@@ -544,15 +616,63 @@ class SliverChildListDelegate extends SliverChildDelegate {
...
@@ -544,15 +616,63 @@ class SliverChildListDelegate extends SliverChildDelegate {
/// The widgets to display.
/// The widgets to display.
final
List
<
Widget
>
children
;
final
List
<
Widget
>
children
;
/// A map to cache key to index lookup for children.
///
/// _keyToIndex[null] is used as current index during the lazy loading process
/// in [_findChildIndex]. _keyToIndex should never be used for looking up null key.
final
Map
<
Key
,
int
>
_keyToIndex
;
bool
get
_isConstantInstance
=>
_keyToIndex
==
null
;
int
_findChildIndex
(
Key
key
)
{
if
(
_isConstantInstance
)
{
return
null
;
}
// Lazily fill the [_keyToIndex].
if
(!
_keyToIndex
.
containsKey
(
key
))
{
int
index
=
_keyToIndex
[
null
];
while
(
index
<
children
.
length
)
{
final
Widget
child
=
children
[
index
];
if
(
child
.
key
!=
null
)
{
_keyToIndex
[
child
.
key
]
=
index
;
}
if
(
child
.
key
==
key
)
{
// Record current index for next function call.
_keyToIndex
[
null
]
=
index
+
1
;
return
index
;
}
index
+=
1
;
}
_keyToIndex
[
null
]
=
index
;
}
else
{
return
_keyToIndex
[
key
];
}
return
null
;
}
@override
int
findIndexByKey
(
Key
key
)
{
assert
(
key
!=
null
);
Key
childKey
;
if
(
key
is
_SaltedValueKey
)
{
final
_SaltedValueKey
saltedValueKey
=
key
;
childKey
=
saltedValueKey
.
value
;
}
else
{
childKey
=
key
;
}
return
_findChildIndex
(
childKey
);
}
@override
@override
Widget
build
(
BuildContext
context
,
int
index
)
{
Widget
build
(
BuildContext
context
,
int
index
)
{
assert
(
children
!=
null
);
assert
(
children
!=
null
);
if
(
index
<
0
||
index
>=
children
.
length
)
if
(
index
<
0
||
index
>=
children
.
length
)
return
null
;
return
null
;
Widget
child
=
children
[
index
];
Widget
child
=
children
[
index
];
final
Key
key
=
child
.
key
!=
null
?
_SaltedValueKey
(
child
.
key
)
:
null
;
assert
(
child
!=
null
);
assert
(
child
!=
null
);
if
(
addRepaintBoundaries
)
if
(
addRepaintBoundaries
)
child
=
RepaintBoundary
.
wrap
(
child
,
index
);
child
=
RepaintBoundary
(
child:
child
);
if
(
addSemanticIndexes
)
{
if
(
addSemanticIndexes
)
{
final
int
semanticIndex
=
semanticIndexCallback
(
child
,
index
);
final
int
semanticIndex
=
semanticIndexCallback
(
child
,
index
);
if
(
semanticIndex
!=
null
)
if
(
semanticIndex
!=
null
)
...
@@ -560,7 +680,7 @@ class SliverChildListDelegate extends SliverChildDelegate {
...
@@ -560,7 +680,7 @@ class SliverChildListDelegate extends SliverChildDelegate {
}
}
if
(
addAutomaticKeepAlives
)
if
(
addAutomaticKeepAlives
)
child
=
AutomaticKeepAlive
(
child:
child
);
child
=
AutomaticKeepAlive
(
child:
child
);
return
child
;
return
KeyedSubtree
(
child:
child
,
key:
key
)
;
}
}
@override
@override
...
@@ -979,9 +1099,15 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
...
@@ -979,9 +1099,15 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
_currentBeforeChild
=
null
;
_currentBeforeChild
=
null
;
assert
(
_currentlyUpdatingChildIndex
==
null
);
assert
(
_currentlyUpdatingChildIndex
==
null
);
try
{
try
{
final
SplayTreeMap
<
int
,
Element
>
newChildren
=
SplayTreeMap
<
int
,
Element
>();
void
processElement
(
int
index
)
{
void
processElement
(
int
index
)
{
_currentlyUpdatingChildIndex
=
index
;
_currentlyUpdatingChildIndex
=
index
;
final
Element
newChild
=
updateChild
(
_childElements
[
index
],
_build
(
index
),
index
);
if
(
_childElements
[
index
]
!=
null
&&
_childElements
[
index
]
!=
newChildren
[
index
])
{
// This index has an old child that isn't used anywhere and should be deactivated.
_childElements
[
index
]
=
updateChild
(
_childElements
[
index
],
null
,
index
);
}
final
Element
newChild
=
updateChild
(
newChildren
[
index
],
_build
(
index
),
index
);
if
(
newChild
!=
null
)
{
if
(
newChild
!=
null
)
{
_childElements
[
index
]
=
newChild
;
_childElements
[
index
]
=
newChild
;
final
SliverMultiBoxAdaptorParentData
parentData
=
newChild
.
renderObject
.
parentData
;
final
SliverMultiBoxAdaptorParentData
parentData
=
newChild
.
renderObject
.
parentData
;
...
@@ -991,14 +1117,32 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
...
@@ -991,14 +1117,32 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
_childElements
.
remove
(
index
);
_childElements
.
remove
(
index
);
}
}
}
}
// processElement may modify the Map - need to do a .toList() here.
_childElements
.
keys
.
toList
().
forEach
(
processElement
);
for
(
int
index
in
_childElements
.
keys
.
toList
())
{
final
Key
key
=
_childElements
[
index
].
widget
.
key
;
final
int
newIndex
=
key
==
null
?
null
:
widget
.
delegate
.
findIndexByKey
(
key
);
if
(
newIndex
!=
null
&&
newIndex
!=
index
)
{
newChildren
[
newIndex
]
=
_childElements
[
index
];
// We need to make sure the original index gets processed.
newChildren
.
putIfAbsent
(
index
,
()
=>
null
);
// We do not want the remapped child to get deactivated during processElement.
_childElements
.
remove
(
index
);
}
else
{
newChildren
.
putIfAbsent
(
index
,
()
=>
_childElements
[
index
]);
}
}
renderObject
.
debugChildIntegrityEnabled
=
false
;
// Moving children will temporary violate the integrity.
newChildren
.
keys
.
forEach
(
processElement
);
if
(
_didUnderflow
)
{
if
(
_didUnderflow
)
{
final
int
lastKey
=
_childElements
.
lastKey
()
??
-
1
;
final
int
lastKey
=
_childElements
.
lastKey
()
??
-
1
;
processElement
(
lastKey
+
1
);
final
int
rightBoundary
=
lastKey
+
1
;
newChildren
[
rightBoundary
]
=
_childElements
[
rightBoundary
];
processElement
(
rightBoundary
);
}
}
}
finally
{
}
finally
{
_currentlyUpdatingChildIndex
=
null
;
_currentlyUpdatingChildIndex
=
null
;
renderObject
.
debugChildIntegrityEnabled
=
true
;
}
}
}
}
...
@@ -1038,7 +1182,6 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
...
@@ -1038,7 +1182,6 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
if
(
oldParentData
!=
newParentData
&&
oldParentData
!=
null
&&
newParentData
!=
null
)
{
if
(
oldParentData
!=
newParentData
&&
oldParentData
!=
null
&&
newParentData
!=
null
)
{
newParentData
.
layoutOffset
=
oldParentData
.
layoutOffset
;
newParentData
.
layoutOffset
=
oldParentData
.
layoutOffset
;
}
}
return
newChild
;
return
newChild
;
}
}
...
@@ -1163,9 +1306,9 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
...
@@ -1163,9 +1306,9 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
@override
@override
void
moveChildRenderObject
(
covariant
RenderObject
child
,
int
slot
)
{
void
moveChildRenderObject
(
covariant
RenderObject
child
,
int
slot
)
{
// TODO(ianh): At some point we should be better about noticing when a
assert
(
slot
!=
null
);
// particular LocalKey changes slot, and handle moving the nodes around.
assert
(
_currentlyUpdatingChildIndex
==
slot
);
assert
(
false
);
renderObject
.
move
(
child
,
after:
_currentBeforeChild
);
}
}
@override
@override
...
...
packages/flutter/test/material/tabs_test.dart
View file @
38808d9f
...
@@ -49,6 +49,7 @@ class StateMarkerState extends State<StateMarker> {
...
@@ -49,6 +49,7 @@ class StateMarkerState extends State<StateMarker> {
}
}
class
AlwaysKeepAliveWidget
extends
StatefulWidget
{
class
AlwaysKeepAliveWidget
extends
StatefulWidget
{
const
AlwaysKeepAliveWidget
({
Key
key
})
:
super
(
key:
key
);
static
String
text
=
'AlwaysKeepAlive'
;
static
String
text
=
'AlwaysKeepAlive'
;
@override
@override
AlwaysKeepAliveState
createState
()
=>
AlwaysKeepAliveState
();
AlwaysKeepAliveState
createState
()
=>
AlwaysKeepAliveState
();
...
@@ -1987,6 +1988,56 @@ void main() {
...
@@ -1987,6 +1988,56 @@ void main() {
});
});
testWidgets
(
'Skipping tabs with global key does not crash'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/24660
final
List
<
String
>
tabs
=
<
String
>[
'Tab1'
,
'Tab2'
,
'Tab3'
,
'Tab4'
,
];
final
TabController
controller
=
TabController
(
vsync:
const
TestVSync
(),
length:
tabs
.
length
,
);
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Align
(
alignment:
Alignment
.
topLeft
,
child:
SizedBox
(
width:
300.0
,
height:
200.0
,
child:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'tabs'
),
bottom:
TabBar
(
controller:
controller
,
tabs:
tabs
.
map
<
Widget
>((
String
tab
)
=>
Tab
(
text:
tab
)).
toList
(),
),
),
body:
TabBarView
(
controller:
controller
,
children:
<
Widget
>[
Text
(
'1'
,
key:
GlobalKey
()),
Text
(
'2'
,
key:
GlobalKey
()),
Text
(
'3'
,
key:
GlobalKey
()),
Text
(
'4'
,
key:
GlobalKey
()),
],
),
),
),
),
),
);
expect
(
find
.
text
(
'1'
),
findsOneWidget
);
expect
(
find
.
text
(
'4'
),
findsNothing
);
await
tester
.
tap
(
find
.
text
(
'Tab4'
));
await
tester
.
pumpAndSettle
();
expect
(
controller
.
index
,
3
);
expect
(
find
.
text
(
'4'
),
findsOneWidget
);
expect
(
find
.
text
(
'1'
),
findsNothing
);
});
testWidgets
(
'Skipping tabs with a KeepAlive child works'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Skipping tabs with a KeepAlive child works'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/11895
// Regression test for https://github.com/flutter/flutter/issues/11895
final
List
<
String
>
tabs
=
<
String
>[
final
List
<
String
>
tabs
=
<
String
>[
...
@@ -2018,7 +2069,7 @@ void main() {
...
@@ -2018,7 +2069,7 @@ void main() {
body:
TabBarView
(
body:
TabBarView
(
controller:
controller
,
controller:
controller
,
children:
<
Widget
>[
children:
<
Widget
>[
AlwaysKeepAliveWidget
(),
AlwaysKeepAliveWidget
(
key:
UniqueKey
()
),
const
Text
(
'2'
),
const
Text
(
'2'
),
const
Text
(
'3'
),
const
Text
(
'3'
),
const
Text
(
'4'
),
const
Text
(
'4'
),
...
...
packages/flutter/test/widgets/box_sliver_mismatch_test.dart
View file @
38808d9f
...
@@ -9,10 +9,10 @@ import 'package:flutter/widgets.dart';
...
@@ -9,10 +9,10 @@ import 'package:flutter/widgets.dart';
void
main
(
)
{
void
main
(
)
{
testWidgets
(
'Sliver in a box'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Sliver in a box'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
const
DecoratedBox
(
DecoratedBox
(
decoration:
BoxDecoration
(),
decoration:
const
BoxDecoration
(),
child:
SliverList
(
child:
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[]),
delegate:
SliverChildListDelegate
(
const
<
Widget
>[]),
),
),
),
),
);
);
...
@@ -21,9 +21,9 @@ void main() {
...
@@ -21,9 +21,9 @@ void main() {
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Row
(
Row
(
children:
const
<
Widget
>[
children:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[]),
delegate:
SliverChildListDelegate
(
const
<
Widget
>[]),
),
),
],
],
),
),
...
...
packages/flutter/test/widgets/framework_test.dart
View file @
38808d9f
...
@@ -451,6 +451,41 @@ void main() {
...
@@ -451,6 +451,41 @@ void main() {
expect
(
count
,
2
);
expect
(
count
,
2
);
});
});
testWidgets
(
'GlobalKey - dettach and re-attach child to different parents'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
Container
(
height:
100
,
child:
CustomScrollView
(
controller:
ScrollController
(),
slivers:
<
Widget
>[
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
Text
(
'child'
,
key:
GlobalKey
()),
]),
)
],
),
),
),
));
final
SliverMultiBoxAdaptorElement
element
=
tester
.
element
(
find
.
byType
(
SliverList
));
Element
childElement
;
// Removing and recreating child with same Global Key should not trigger
// duplicate key error.
element
.
visitChildren
((
Element
e
)
{
childElement
=
e
;
});
element
.
removeChild
(
childElement
.
renderObject
);
element
.
createChild
(
0
,
after:
null
);
element
.
visitChildren
((
Element
e
)
{
childElement
=
e
;
});
element
.
removeChild
(
childElement
.
renderObject
);
element
.
createChild
(
0
,
after:
null
);
});
testWidgets
(
'Defunct setState throws exception'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Defunct setState throws exception'
,
(
WidgetTester
tester
)
async
{
StateSetter
setState
;
StateSetter
setState
;
...
...
packages/flutter/test/widgets/slivers_appbar_floating_pinned_test.dart
View file @
38808d9f
...
@@ -22,12 +22,12 @@ void main() {
...
@@ -22,12 +22,12 @@ void main() {
data:
const
MediaQueryData
(),
data:
const
MediaQueryData
(),
child:
CustomScrollView
(
child:
CustomScrollView
(
controller:
controller
,
controller:
controller
,
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverAppBar
(
floating:
true
,
pinned:
true
,
expandedHeight:
200.0
,
title:
Text
(
'A'
)),
const
SliverAppBar
(
floating:
true
,
pinned:
true
,
expandedHeight:
200.0
,
title:
Text
(
'A'
)),
SliverAppBar
(
primary:
false
,
pinned:
true
,
title:
Text
(
'B'
)),
const
SliverAppBar
(
primary:
false
,
pinned:
true
,
title:
Text
(
'B'
)),
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(
delegate:
SliverChildListDelegate
(
<
Widget
>[
const
<
Widget
>[
Text
(
'C'
),
Text
(
'C'
),
Text
(
'D'
),
Text
(
'D'
),
SizedBox
(
height:
500.0
),
SizedBox
(
height:
500.0
),
...
...
packages/flutter/test/widgets/slivers_appbar_floating_test.dart
View file @
38808d9f
...
@@ -203,9 +203,9 @@ void main() {
...
@@ -203,9 +203,9 @@ void main() {
physics:
const
BouncingScrollPhysics
(),
physics:
const
BouncingScrollPhysics
(),
slivers:
<
Widget
>[
slivers:
<
Widget
>[
SliverPersistentHeader
(
delegate:
TestDelegate
(),
floating:
true
),
SliverPersistentHeader
(
delegate:
TestDelegate
(),
floating:
true
),
const
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(<
Widget
>[
SizedBox
(
const
SizedBox
(
height:
300.0
,
height:
300.0
,
child:
Text
(
'X'
),
child:
Text
(
'X'
),
),
),
...
...
packages/flutter/test/widgets/slivers_appbar_pinned_test.dart
View file @
38808d9f
...
@@ -258,9 +258,9 @@ void main() {
...
@@ -258,9 +258,9 @@ void main() {
physics:
const
BouncingScrollPhysics
(),
physics:
const
BouncingScrollPhysics
(),
slivers:
<
Widget
>[
slivers:
<
Widget
>[
SliverPersistentHeader
(
delegate:
TestDelegate
(),
pinned:
true
),
SliverPersistentHeader
(
delegate:
TestDelegate
(),
pinned:
true
),
const
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(<
Widget
>[
SizedBox
(
const
SizedBox
(
height:
300.0
,
height:
300.0
,
child:
Text
(
'X'
),
child:
Text
(
'X'
),
),
),
...
...
packages/flutter/test/widgets/slivers_appbar_scrolling_test.dart
View file @
38808d9f
...
@@ -82,8 +82,40 @@ void main() {
...
@@ -82,8 +82,40 @@ void main() {
physics:
const
BouncingScrollPhysics
(),
physics:
const
BouncingScrollPhysics
(),
slivers:
<
Widget
>[
slivers:
<
Widget
>[
SliverPersistentHeader
(
delegate:
TestDelegate
()),
SliverPersistentHeader
(
delegate:
TestDelegate
()),
const
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(<
Widget
>[
const
SizedBox
(
height:
300.0
,
child:
Text
(
'X'
),
),
]),
),
],
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Container
)),
Offset
.
zero
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'X'
)),
const
Offset
(
0.0
,
200.0
));
final
ScrollPosition
position
=
tester
.
state
<
ScrollableState
>(
find
.
byType
(
Scrollable
)).
position
;
position
.
jumpTo
(-
50.0
);
await
tester
.
pump
();
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Container
)),
Offset
.
zero
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'X'
)),
const
Offset
(
0.0
,
250.0
));
});
testWidgets
(
'Sliver appbars const child delegate - scrolling - overscroll gap is below header'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CustomScrollView
(
physics:
const
BouncingScrollPhysics
(),
slivers:
<
Widget
>[
SliverPersistentHeader
(
delegate:
TestDelegate
()),
const
SliverList
(
delegate:
SliverChildListDelegate
.
fixed
(<
Widget
>[
SizedBox
(
SizedBox
(
height:
300.0
,
height:
300.0
,
child:
Text
(
'X'
),
child:
Text
(
'X'
),
...
...
packages/flutter/test/widgets/slivers_block_test.dart
View file @
38808d9f
...
@@ -9,6 +9,28 @@ import 'package:flutter/widgets.dart';
...
@@ -9,6 +9,28 @@ import 'package:flutter/widgets.dart';
import
'../rendering/mock_canvas.dart'
;
import
'../rendering/mock_canvas.dart'
;
Future
<
void
>
test
(
WidgetTester
tester
,
double
offset
)
{
Future
<
void
>
test
(
WidgetTester
tester
,
double
offset
)
{
return
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
offset:
ViewportOffset
.
fixed
(
offset
),
slivers:
<
Widget
>[
SliverList
(
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'b'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'c'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'd'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'e'
)),
]),
),
],
),
),
);
}
Future
<
void
>
testWithConstChildDelegate
(
WidgetTester
tester
,
double
offset
)
{
return
tester
.
pumpWidget
(
return
tester
.
pumpWidget
(
Directionality
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
...
@@ -16,7 +38,7 @@ Future<void> test(WidgetTester tester, double offset) {
...
@@ -16,7 +38,7 @@ Future<void> test(WidgetTester tester, double offset) {
offset:
ViewportOffset
.
fixed
(
offset
),
offset:
ViewportOffset
.
fixed
(
offset
),
slivers:
const
<
Widget
>[
slivers:
const
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
.
fixed
(<
Widget
>[
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'b'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'b'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'c'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'c'
)),
...
@@ -76,6 +98,39 @@ void main() {
...
@@ -76,6 +98,39 @@ void main() {
],
'ab'
);
],
'ab'
);
});
});
testWidgets
(
'Viewport+SliverBlock basic test with constant SliverChildListDelegate'
,
(
WidgetTester
tester
)
async
{
await
testWithConstChildDelegate
(
tester
,
0.0
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
Viewport
)).
size
,
equals
(
const
Size
(
800.0
,
600.0
)));
verify
(
tester
,
<
Offset
>[
const
Offset
(
0.0
,
0.0
),
const
Offset
(
0.0
,
400.0
),
],
'ab'
);
await
testWithConstChildDelegate
(
tester
,
200.0
);
verify
(
tester
,
<
Offset
>[
const
Offset
(
0.0
,
-
200.0
),
const
Offset
(
0.0
,
200.0
),
],
'ab'
);
await
testWithConstChildDelegate
(
tester
,
600.0
);
verify
(
tester
,
<
Offset
>[
const
Offset
(
0.0
,
-
200.0
),
const
Offset
(
0.0
,
200.0
),
],
'bc'
);
await
testWithConstChildDelegate
(
tester
,
900.0
);
verify
(
tester
,
<
Offset
>[
const
Offset
(
0.0
,
-
100.0
),
const
Offset
(
0.0
,
300.0
),
],
'cd'
);
await
testWithConstChildDelegate
(
tester
,
200.0
);
verify
(
tester
,
<
Offset
>[
const
Offset
(
0.0
,
-
200.0
),
const
Offset
(
0.0
,
200.0
),
],
'ab'
);
});
testWidgets
(
'Viewport with GlobalKey reparenting'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Viewport with GlobalKey reparenting'
,
(
WidgetTester
tester
)
async
{
final
Key
key1
=
GlobalKey
();
final
Key
key1
=
GlobalKey
();
final
ViewportOffset
offset
=
ViewportOffset
.
zero
();
final
ViewportOffset
offset
=
ViewportOffset
.
zero
();
...
@@ -150,9 +205,9 @@ void main() {
...
@@ -150,9 +205,9 @@ void main() {
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
child:
Viewport
(
offset:
offset
,
offset:
offset
,
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
251.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
251.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
252.0
,
child:
Text
(
'b'
)),
SizedBox
(
height:
252.0
,
child:
Text
(
'b'
)),
]),
]),
...
@@ -261,9 +316,9 @@ void main() {
...
@@ -261,9 +316,9 @@ void main() {
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
child:
Viewport
(
offset:
ViewportOffset
.
zero
(),
offset:
ViewportOffset
.
zero
(),
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
]),
]),
),
),
...
@@ -279,9 +334,9 @@ void main() {
...
@@ -279,9 +334,9 @@ void main() {
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
child:
Viewport
(
offset:
ViewportOffset
.
fixed
(
100.0
),
offset:
ViewportOffset
.
fixed
(
100.0
),
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
400.0
,
child:
Text
(
'a'
)),
]),
]),
),
),
...
@@ -297,9 +352,9 @@ void main() {
...
@@ -297,9 +352,9 @@ void main() {
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
child:
Viewport
(
offset:
ViewportOffset
.
fixed
(
100.0
),
offset:
ViewportOffset
.
fixed
(
100.0
),
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
4000.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
4000.0
,
child:
Text
(
'a'
)),
]),
]),
),
),
...
@@ -315,9 +370,9 @@ void main() {
...
@@ -315,9 +370,9 @@ void main() {
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
child:
Viewport
(
child:
Viewport
(
offset:
ViewportOffset
.
zero
(),
offset:
ViewportOffset
.
zero
(),
slivers:
const
<
Widget
>[
slivers:
<
Widget
>[
SliverList
(
SliverList
(
delegate:
SliverChildListDelegate
(<
Widget
>[
delegate:
SliverChildListDelegate
(
const
<
Widget
>[
SizedBox
(
height:
4000.0
,
child:
Text
(
'a'
)),
SizedBox
(
height:
4000.0
,
child:
Text
(
'a'
)),
]),
]),
),
),
...
...
packages/flutter/test/widgets/slivers_keepalive_test.dart
0 → 100644
View file @
38808d9f
// Copyright 2019 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
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/rendering.dart'
;
void
main
(
)
{
testWidgets
(
'Sliver with keep alive without key - should dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver without keep alive without key - should dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
),
WidgetTest1
(
text:
'child 1'
),
WidgetTest2
(
text:
'child 2'
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver without keep alive with key - should dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
GlobalKey
()),
WidgetTest1
(
text:
'child 1'
,
key:
GlobalKey
()),
WidgetTest2
(
text:
'child 2'
,
key:
GlobalKey
()),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver with keep alive with key - should not dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
GlobalKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
GlobalKey
(),
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
key:
GlobalKey
(),
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver with keep alive with Unique key - should not dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
key:
UniqueKey
(),
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver with keep alive with Value key - should not dispose after reodering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
const
ValueKey
<
int
>(
0
),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
const
ValueKey
<
int
>(
1
),
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
key:
const
ValueKey
<
int
>(
2
),
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'Sliver complex case 1'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
GlobalKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
GlobalKey
(),
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
false
);
// Child 2 does not have a key.
expect
(
state2
.
hasBeenDisposed
,
true
);
});
testWidgets
(
'Sliver complex case 2'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
GlobalKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
UniqueKey
()),
WidgetTest2
(
text:
'child 2'
,
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
true
);
});
testWidgets
(
'Sliver with SliverChildBuilderDelegate'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
GlobalKey
()),
WidgetTest2
(
text:
'child 2'
,
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildBuilderTest
(
children:
childList
));
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildBuilderTest
(
children:
childList
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildBuilderTest
(
children:
childList
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildBuilderTest
(
children:
childList
));
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 0'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildBuilderTest
(
children:
childList
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
,
skipOffstage:
false
),
findsNothing
);
expect
(
find
.
text
(
'child 2'
,
skipOffstage:
false
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
true
);
});
testWidgets
(
'SliverFillViewport should not dispose widget with key during in screen reordering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
key:
UniqueKey
()),
WidgetTest2
(
text:
'child 2'
,
keepAlive:
true
),
];
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingChildListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
false
);
expect
(
state2
.
hasBeenDisposed
,
true
);
});
testWidgets
(
'SliverList should not dispose widget with key during in screen reordering'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
key:
UniqueKey
()),
];
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
1
,
2
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
0
,
2
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
false
);
});
testWidgets
(
'SliverList remove child from child list'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
childList
=
<
Widget
>[
WidgetTest0
(
text:
'child 0'
,
key:
UniqueKey
(),
keepAlive:
true
),
WidgetTest1
(
text:
'child 1'
,
keepAlive:
true
),
WidgetTest2
(
text:
'child 2'
,
key:
UniqueKey
()),
];
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
final
_WidgetTest0State
state0
=
tester
.
state
(
find
.
byType
(
WidgetTest0
));
final
_WidgetTest1State
state1
=
tester
.
state
(
find
.
byType
(
WidgetTest1
));
final
_WidgetTest2State
state2
=
tester
.
state
(
find
.
byType
(
WidgetTest2
));
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
),
findsOneWidget
);
childList
=
createSwitchedChildList
(
childList
,
0
,
1
);
childList
.
removeAt
(
2
);
await
tester
.
pumpWidget
(
SwitchingSliverListTest
(
children:
childList
,
viewportFraction:
0.1
)
);
expect
(
find
.
text
(
'child 0'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'child 2'
),
findsNothing
);
expect
(
state0
.
hasBeenDisposed
,
false
);
expect
(
state1
.
hasBeenDisposed
,
true
);
expect
(
state2
.
hasBeenDisposed
,
true
);
});
}
List
<
Widget
>
createSwitchedChildList
(
List
<
Widget
>
childList
,
int
i
,
int
j
)
{
final
Widget
w
=
childList
[
i
];
childList
[
i
]
=
childList
[
j
];
childList
[
j
]
=
w
;
return
List
<
Widget
>.
from
(
childList
);
}
class
SwitchingChildBuilderTest
extends
StatefulWidget
{
SwitchingChildBuilderTest
({
this
.
children
,
Key
key
})
:
super
(
key:
key
);
final
List
<
Widget
>
children
;
@override
_SwitchingChildBuilderTest
createState
()
=>
_SwitchingChildBuilderTest
();
}
class
_SwitchingChildBuilderTest
extends
State
<
SwitchingChildBuilderTest
>
{
List
<
Widget
>
children
;
Map
<
Key
,
int
>
_mapKeyToIndex
;
@override
void
initState
()
{
super
.
initState
();
children
=
widget
.
children
;
_mapKeyToIndex
=
<
Key
,
int
>{};
for
(
int
index
=
0
;
index
<
children
.
length
;
index
+=
1
)
{
final
Key
key
=
children
[
index
].
key
;
if
(
key
!=
null
)
{
_mapKeyToIndex
[
key
]
=
index
;
}
}
}
@override
void
didUpdateWidget
(
SwitchingChildBuilderTest
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
oldWidget
.
children
!=
widget
.
children
)
{
children
=
widget
.
children
;
_mapKeyToIndex
=
<
Key
,
int
>{};
for
(
int
index
=
0
;
index
<
children
.
length
;
index
+=
1
)
{
final
Key
key
=
children
[
index
].
key
;
if
(
key
!=
null
)
{
_mapKeyToIndex
[
key
]
=
index
;
}
}
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
Container
(
height:
100
,
child:
CustomScrollView
(
cacheExtent:
0
,
slivers:
<
Widget
>[
SliverFillViewport
(
delegate:
SliverChildBuilderDelegate
(
(
BuildContext
context
,
int
index
)
{
return
children
[
index
];
},
childCount:
children
.
length
,
findChildIndexCallback:
(
Key
key
)
{
return
_mapKeyToIndex
[
key
]
==
null
?
-
1
:
_mapKeyToIndex
[
key
];
}
),
)
],
),
),
),
);
}
}
class
SwitchingChildListTest
extends
StatefulWidget
{
SwitchingChildListTest
({
this
.
children
,
this
.
viewportFraction
=
1.0
,
Key
key
})
:
super
(
key:
key
);
final
List
<
Widget
>
children
;
final
double
viewportFraction
;
@override
_SwitchingChildListTest
createState
()
=>
_SwitchingChildListTest
();
}
class
_SwitchingChildListTest
extends
State
<
SwitchingChildListTest
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
Container
(
height:
100
,
child:
CustomScrollView
(
cacheExtent:
0
,
slivers:
<
Widget
>[
SliverFillViewport
(
viewportFraction:
widget
.
viewportFraction
,
delegate:
SliverChildListDelegate
(
widget
.
children
),
)
],
),
),
),
);
}
}
class
SwitchingSliverListTest
extends
StatefulWidget
{
SwitchingSliverListTest
({
this
.
children
,
this
.
viewportFraction
=
1.0
,
Key
key
})
:
super
(
key:
key
);
final
List
<
Widget
>
children
;
final
double
viewportFraction
;
@override
_SwitchingSliverListTest
createState
()
=>
_SwitchingSliverListTest
();
}
class
_SwitchingSliverListTest
extends
State
<
SwitchingSliverListTest
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
Container
(
height:
100
,
child:
CustomScrollView
(
cacheExtent:
0
,
slivers:
<
Widget
>[
SliverList
(
delegate:
SliverChildListDelegate
(
widget
.
children
),
)
],
),
),
),
);
}
}
class
WidgetTest0
extends
StatefulWidget
{
WidgetTest0
({
this
.
text
,
this
.
keepAlive
=
false
,
Key
key
})
:
super
(
key:
key
);
final
String
text
;
final
bool
keepAlive
;
@override
_WidgetTest0State
createState
()
=>
_WidgetTest0State
();
}
class
_WidgetTest0State
extends
State
<
WidgetTest0
>
with
AutomaticKeepAliveClientMixin
{
bool
hasBeenDisposed
=
false
;
@override
Widget
build
(
BuildContext
context
)
{
super
.
build
(
context
);
return
Text
(
widget
.
text
);
}
@override
void
dispose
()
{
hasBeenDisposed
=
true
;
super
.
dispose
();
}
@override
bool
get
wantKeepAlive
=>
widget
.
keepAlive
;
}
class
WidgetTest1
extends
StatefulWidget
{
WidgetTest1
({
this
.
text
,
this
.
keepAlive
=
false
,
Key
key
})
:
super
(
key:
key
);
final
String
text
;
final
bool
keepAlive
;
@override
_WidgetTest1State
createState
()
=>
_WidgetTest1State
();
}
class
_WidgetTest1State
extends
State
<
WidgetTest1
>
with
AutomaticKeepAliveClientMixin
{
bool
hasBeenDisposed
=
false
;
@override
Widget
build
(
BuildContext
context
)
{
super
.
build
(
context
);
return
Text
(
widget
.
text
);
}
@override
void
dispose
()
{
hasBeenDisposed
=
true
;
super
.
dispose
();
}
@override
bool
get
wantKeepAlive
=>
widget
.
keepAlive
;
}
class
WidgetTest2
extends
StatefulWidget
{
WidgetTest2
({
this
.
text
,
this
.
keepAlive
=
false
,
Key
key
})
:
super
(
key:
key
);
final
String
text
;
final
bool
keepAlive
;
@override
_WidgetTest2State
createState
()
=>
_WidgetTest2State
();
}
class
_WidgetTest2State
extends
State
<
WidgetTest2
>
with
AutomaticKeepAliveClientMixin
{
bool
hasBeenDisposed
=
false
;
@override
Widget
build
(
BuildContext
context
)
{
super
.
build
(
context
);
return
Text
(
widget
.
text
);
}
@override
void
dispose
()
{
hasBeenDisposed
=
true
;
super
.
dispose
();
}
@override
bool
get
wantKeepAlive
=>
widget
.
keepAlive
;
}
packages/flutter/test/widgets/slivers_test.dart
View file @
38808d9f
...
@@ -206,7 +206,8 @@ void main() {
...
@@ -206,7 +206,8 @@ void main() {
addRepaintBoundaries:
false
,
addRepaintBoundaries:
false
,
addSemanticIndexes:
false
,
addSemanticIndexes:
false
,
);
);
expect
(
builderThrowsDelegate
.
build
(
null
,
0
),
errorText
);
final
KeyedSubtree
wrapped
=
builderThrowsDelegate
.
build
(
null
,
0
);
expect
(
wrapped
.
child
,
errorText
);
expect
(
tester
.
takeException
(),
'builder'
);
expect
(
tester
.
takeException
(),
'builder'
);
ErrorWidget
.
builder
=
oldBuilder
;
ErrorWidget
.
builder
=
oldBuilder
;
});
});
...
...
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