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
52c665d2
Unverified
Commit
52c665d2
authored
Jan 23, 2020
by
Maurice Parrish
Committed by
GitHub
Jan 23, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Do not rebuild Routes when a new opaque Route is pushed on top (#48900)" (#49366)
This reverts commit
8eecdbe8
.
parent
a1fa1a3d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
277 additions
and
702 deletions
+277
-702
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+44
-58
overlay.dart
packages/flutter/lib/src/widgets/overlay.dart
+169
-278
nav_bar_transition_test.dart
packages/flutter/test/cupertino/nav_bar_transition_test.dart
+5
-2
debug_test.dart
packages/flutter/test/material/debug_test.dart
+2
-2
stepper_test.dart
packages/flutter/test/material/stepper_test.dart
+3
-4
navigator_test.dart
packages/flutter/test/widgets/navigator_test.dart
+0
-44
overlay_test.dart
packages/flutter/test/widgets/overlay_test.dart
+54
-314
No files found.
packages/flutter/lib/src/rendering/stack.dart
View file @
52c665d2
...
...
@@ -425,8 +425,7 @@ class RenderStack extends RenderBox
}
}
/// Helper function for calculating the intrinsics metrics of a Stack.
static
double
getIntrinsicDimension
(
RenderBox
firstChild
,
double
mainChildSizeGetter
(
RenderBox
child
))
{
double
_getIntrinsicDimension
(
double
mainChildSizeGetter
(
RenderBox
child
))
{
double
extent
=
0.0
;
RenderBox
child
=
firstChild
;
while
(
child
!=
null
)
{
...
...
@@ -441,22 +440,22 @@ class RenderStack extends RenderBox
@override
double
computeMinIntrinsicWidth
(
double
height
)
{
return
getIntrinsicDimension
(
firstChild
,
(
RenderBox
child
)
=>
child
.
getMinIntrinsicWidth
(
height
));
return
_getIntrinsicDimension
(
(
RenderBox
child
)
=>
child
.
getMinIntrinsicWidth
(
height
));
}
@override
double
computeMaxIntrinsicWidth
(
double
height
)
{
return
getIntrinsicDimension
(
firstChild
,
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicWidth
(
height
));
return
_getIntrinsicDimension
(
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicWidth
(
height
));
}
@override
double
computeMinIntrinsicHeight
(
double
width
)
{
return
getIntrinsicDimension
(
firstChild
,
(
RenderBox
child
)
=>
child
.
getMinIntrinsicHeight
(
width
));
return
_getIntrinsicDimension
(
(
RenderBox
child
)
=>
child
.
getMinIntrinsicHeight
(
width
));
}
@override
double
computeMaxIntrinsicHeight
(
double
width
)
{
return
getIntrinsicDimension
(
firstChild
,
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicHeight
(
width
));
return
_getIntrinsicDimension
(
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicHeight
(
width
));
}
@override
...
...
@@ -464,57 +463,6 @@ class RenderStack extends RenderBox
return
defaultComputeDistanceToHighestActualBaseline
(
baseline
);
}
/// Lays out the positioned `child` according to `alignment` within a Stack of `size`.
///
/// Returns true when the child has visual overflow.
static
bool
layoutPositionedChild
(
RenderBox
child
,
StackParentData
childParentData
,
Size
size
,
Alignment
alignment
)
{
assert
(
childParentData
.
isPositioned
);
assert
(
child
.
parentData
==
childParentData
);
bool
hasVisualOverflow
=
false
;
BoxConstraints
childConstraints
=
const
BoxConstraints
();
if
(
childParentData
.
left
!=
null
&&
childParentData
.
right
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
width:
size
.
width
-
childParentData
.
right
-
childParentData
.
left
);
else
if
(
childParentData
.
width
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
width:
childParentData
.
width
);
if
(
childParentData
.
top
!=
null
&&
childParentData
.
bottom
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
height:
size
.
height
-
childParentData
.
bottom
-
childParentData
.
top
);
else
if
(
childParentData
.
height
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
height:
childParentData
.
height
);
child
.
layout
(
childConstraints
,
parentUsesSize:
true
);
double
x
;
if
(
childParentData
.
left
!=
null
)
{
x
=
childParentData
.
left
;
}
else
if
(
childParentData
.
right
!=
null
)
{
x
=
size
.
width
-
childParentData
.
right
-
child
.
size
.
width
;
}
else
{
x
=
alignment
.
alongOffset
(
size
-
child
.
size
as
Offset
).
dx
;
}
if
(
x
<
0.0
||
x
+
child
.
size
.
width
>
size
.
width
)
hasVisualOverflow
=
true
;
double
y
;
if
(
childParentData
.
top
!=
null
)
{
y
=
childParentData
.
top
;
}
else
if
(
childParentData
.
bottom
!=
null
)
{
y
=
size
.
height
-
childParentData
.
bottom
-
child
.
size
.
height
;
}
else
{
y
=
alignment
.
alongOffset
(
size
-
child
.
size
as
Offset
).
dy
;
}
if
(
y
<
0.0
||
y
+
child
.
size
.
height
>
size
.
height
)
hasVisualOverflow
=
true
;
childParentData
.
offset
=
Offset
(
x
,
y
);
return
hasVisualOverflow
;
}
@override
void
performLayout
()
{
_resolve
();
...
...
@@ -579,7 +527,45 @@ class RenderStack extends RenderBox
if
(!
childParentData
.
isPositioned
)
{
childParentData
.
offset
=
_resolvedAlignment
.
alongOffset
(
size
-
child
.
size
as
Offset
);
}
else
{
_hasVisualOverflow
=
layoutPositionedChild
(
child
,
childParentData
,
size
,
_resolvedAlignment
)
||
_hasVisualOverflow
;
BoxConstraints
childConstraints
=
const
BoxConstraints
();
if
(
childParentData
.
left
!=
null
&&
childParentData
.
right
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
width:
size
.
width
-
childParentData
.
right
-
childParentData
.
left
);
else
if
(
childParentData
.
width
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
width:
childParentData
.
width
);
if
(
childParentData
.
top
!=
null
&&
childParentData
.
bottom
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
height:
size
.
height
-
childParentData
.
bottom
-
childParentData
.
top
);
else
if
(
childParentData
.
height
!=
null
)
childConstraints
=
childConstraints
.
tighten
(
height:
childParentData
.
height
);
child
.
layout
(
childConstraints
,
parentUsesSize:
true
);
double
x
;
if
(
childParentData
.
left
!=
null
)
{
x
=
childParentData
.
left
;
}
else
if
(
childParentData
.
right
!=
null
)
{
x
=
size
.
width
-
childParentData
.
right
-
child
.
size
.
width
;
}
else
{
x
=
_resolvedAlignment
.
alongOffset
(
size
-
child
.
size
as
Offset
).
dx
;
}
if
(
x
<
0.0
||
x
+
child
.
size
.
width
>
size
.
width
)
_hasVisualOverflow
=
true
;
double
y
;
if
(
childParentData
.
top
!=
null
)
{
y
=
childParentData
.
top
;
}
else
if
(
childParentData
.
bottom
!=
null
)
{
y
=
size
.
height
-
childParentData
.
bottom
-
child
.
size
.
height
;
}
else
{
y
=
_resolvedAlignment
.
alongOffset
(
size
-
child
.
size
as
Offset
).
dy
;
}
if
(
y
<
0.0
||
y
+
child
.
size
.
height
>
size
.
height
)
_hasVisualOverflow
=
true
;
childParentData
.
offset
=
Offset
(
x
,
y
);
}
assert
(
child
.
parentData
==
childParentData
);
...
...
packages/flutter/lib/src/widgets/overlay.dart
View file @
52c665d2
...
...
@@ -4,13 +4,13 @@
import
'dart:async'
;
import
'dart:collection'
;
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'basic.dart'
;
import
'debug.dart'
;
import
'framework.dart'
;
import
'ticker_provider.dart'
;
...
...
@@ -115,7 +115,7 @@ class OverlayEntry {
}
OverlayState
_overlay
;
final
GlobalKey
<
_OverlayEntry
WidgetState
>
_key
=
GlobalKey
<
_OverlayEntryWidget
State
>();
final
GlobalKey
<
_OverlayEntry
State
>
_key
=
GlobalKey
<
_OverlayEntry
State
>();
/// Remove this entry from the overlay.
///
...
...
@@ -152,30 +152,21 @@ class OverlayEntry {
String
toString
()
=>
'
${describeIdentity(this)}
(opaque:
$opaque
; maintainState:
$maintainState
)'
;
}
class
_OverlayEntryWidget
extends
StatefulWidget
{
const
_OverlayEntryWidget
({
@required
Key
key
,
@required
this
.
entry
,
this
.
tickerEnabled
=
true
,
})
:
assert
(
key
!=
null
),
assert
(
entry
!=
null
),
assert
(
tickerEnabled
!=
null
),
super
(
key:
key
);
class
_OverlayEntry
extends
StatefulWidget
{
_OverlayEntry
(
this
.
entry
)
:
assert
(
entry
!=
null
),
super
(
key:
entry
.
_key
);
final
OverlayEntry
entry
;
final
bool
tickerEnabled
;
@override
_OverlayEntry
WidgetState
createState
()
=>
_OverlayEntryWidget
State
();
_OverlayEntry
State
createState
()
=>
_OverlayEntry
State
();
}
class
_OverlayEntry
WidgetState
extends
State
<
_OverlayEntryWidget
>
{
class
_OverlayEntry
State
extends
State
<
_OverlayEntry
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
TickerMode
(
enabled:
widget
.
tickerEnabled
,
child:
widget
.
entry
.
builder
(
context
),
);
return
widget
.
entry
.
builder
(
context
);
}
void
_markNeedsBuild
()
{
...
...
@@ -461,32 +452,28 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
@override
Widget
build
(
BuildContext
context
)
{
// This list is filled backwards and then reversed below before
// it is added to the tree.
final
List
<
Widget
>
children
=
<
Widget
>[];
// These lists are filled backwards. For the offstage children that
// does not matter since they aren't rendered, but for the onstage
// children we reverse the list below before adding it to the tree.
final
List
<
Widget
>
onstageChildren
=
<
Widget
>[];
final
List
<
Widget
>
offstageChildren
=
<
Widget
>[];
bool
onstage
=
true
;
int
onstageCount
=
0
;
for
(
int
i
=
_entries
.
length
-
1
;
i
>=
0
;
i
-=
1
)
{
final
OverlayEntry
entry
=
_entries
[
i
];
if
(
onstage
)
{
onstageCount
+=
1
;
children
.
add
(
_OverlayEntryWidget
(
key:
entry
.
_key
,
entry:
entry
,
));
onstageChildren
.
add
(
_OverlayEntry
(
entry
));
if
(
entry
.
opaque
)
onstage
=
false
;
}
else
if
(
entry
.
maintainState
)
{
children
.
add
(
_OverlayEntryWidget
(
key:
entry
.
_key
,
entry:
entry
,
tickerEnabled:
false
,
));
offstageChildren
.
add
(
TickerMode
(
enabled:
false
,
child:
_OverlayEntry
(
entry
)));
}
}
return
_Theatre
(
skipCount:
children
.
length
-
onstageCount
,
children:
children
.
reversed
.
toList
(
growable:
false
),
onstage:
Stack
(
fit:
StackFit
.
expand
,
children:
onstageChildren
.
reversed
.
toList
(
growable:
false
),
),
offstage:
offstageChildren
,
);
}
...
...
@@ -499,50 +486,36 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
}
}
/// Special version of a [Stack], that doesn't layout and render the first
/// [skipCount] children.
/// A widget that has one [onstage] child which is visible, and one or more
/// [offstage] widgets which are kept alive, and are built, but are not laid out
/// or painted.
///
/// The first [skipCount] children are considered "offstage".
class
_Theatre
extends
MultiChildRenderObjectWidget
{
/// The onstage widget must be a [Stack].
///
/// For convenience, it is legal to use [Positioned] widgets around the offstage
/// widgets.
class
_Theatre
extends
RenderObjectWidget
{
_Theatre
({
Key
key
,
this
.
skipCount
=
0
,
List
<
Widget
>
children
=
const
<
Widget
>[],
})
:
assert
(
skipCount
!=
null
),
assert
(
skipCount
>=
0
),
assert
(
children
!=
null
),
assert
(
children
.
length
>=
skipCount
),
super
(
key:
key
,
children:
children
);
this
.
onstage
,
@required
this
.
offstage
,
})
:
assert
(
offstage
!=
null
),
assert
(!
offstage
.
any
((
Widget
child
)
=>
child
==
null
));
final
int
skipCount
;
@override
_TheatreElement
createElement
()
=>
_TheatreElement
(
this
);
final
Stack
onstage
;
@override
_RenderTheatre
createRenderObject
(
BuildContext
context
)
{
return
_RenderTheatre
(
skipCount:
skipCount
,
textDirection:
Directionality
.
of
(
context
),
);
}
final
List
<
Widget
>
offstage
;
@override
void
updateRenderObject
(
BuildContext
context
,
_RenderTheatre
renderObject
)
{
renderObject
..
skipCount
=
skipCount
..
textDirection
=
Directionality
.
of
(
context
);
}
_TheatreElement
createElement
()
=>
_TheatreElement
(
this
);
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
IntProperty
(
'skipCount'
,
skipCount
));
}
_RenderTheatre
createRenderObject
(
BuildContext
context
)
=>
_RenderTheatre
();
}
class
_TheatreElement
extends
MultiChildRenderObjectElement
{
_TheatreElement
(
_Theatre
widget
)
:
super
(
widget
);
class
_TheatreElement
extends
RenderObjectElement
{
_TheatreElement
(
_Theatre
widget
)
:
assert
(!
debugChildrenHaveDuplicateKeys
(
widget
,
widget
.
offstage
)),
super
(
widget
);
@override
_Theatre
get
widget
=>
super
.
widget
as
_Theatre
;
...
...
@@ -550,268 +523,186 @@ class _TheatreElement extends MultiChildRenderObjectElement {
@override
_RenderTheatre
get
renderObject
=>
super
.
renderObject
as
_RenderTheatre
;
@override
void
debugVisitOnstageChildren
(
ElementVisitor
visitor
)
{
assert
(
children
.
length
>=
widget
.
skipCount
);
children
.
skip
(
widget
.
skipCount
).
forEach
(
visitor
);
}
}
class
_RenderTheatre
extends
RenderBox
with
ContainerRenderObjectMixin
<
RenderBox
,
StackParentData
>
{
_RenderTheatre
({
List
<
RenderBox
>
children
,
@required
TextDirection
textDirection
,
int
skipCount
=
0
,
})
:
assert
(
skipCount
!=
null
),
assert
(
skipCount
>=
0
),
assert
(
textDirection
!=
null
),
_textDirection
=
textDirection
,
_skipCount
=
skipCount
{
addAll
(
children
);
}
Element
_onstage
;
static
final
Object
_onstageSlot
=
Object
();
bool
_hasVisualOverflow
=
false
;
List
<
Element
>
_offstage
;
final
Set
<
Element
>
_forgottenOffstageChildren
=
HashSet
<
Element
>();
@override
void
setupParentData
(
RenderBox
child
)
{
if
(
child
.
parentData
is
!
StackParentData
)
child
.
parentData
=
StackParentData
();
}
Alignment
_resolvedAlignment
;
void
_resolve
()
{
if
(
_resolvedAlignment
!=
null
)
return
;
_resolvedAlignment
=
AlignmentDirectional
.
topStart
.
resolve
(
textDirection
);
}
void
_markNeedResolution
()
{
_resolvedAlignment
=
null
;
markNeedsLayout
();
}
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_markNeedResolution
();
}
int
get
skipCount
=>
_skipCount
;
int
_skipCount
;
set
skipCount
(
int
value
)
{
assert
(
value
!=
null
);
if
(
_skipCount
!=
value
)
{
_skipCount
=
value
;
markNeedsLayout
();
void
insertChildRenderObject
(
RenderBox
child
,
dynamic
slot
)
{
assert
(
renderObject
.
debugValidateChild
(
child
));
if
(
slot
==
_onstageSlot
)
{
assert
(
child
is
RenderStack
);
renderObject
.
child
=
child
as
RenderStack
;
}
else
{
assert
(
slot
==
null
||
slot
is
Element
);
renderObject
.
insert
(
child
,
after:
slot
?.
renderObject
as
RenderBox
);
}
}
RenderBox
get
_firstOnstageChild
{
if
(
skipCount
==
super
.
childCount
)
{
return
null
;
}
RenderBox
child
=
super
.
firstChild
;
for
(
int
toSkip
=
skipCount
;
toSkip
>
0
;
toSkip
--)
{
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
child
=
childParentData
.
nextSibling
;
assert
(
child
!=
null
);
@override
void
moveChildRenderObject
(
RenderBox
child
,
dynamic
slot
)
{
if
(
slot
==
_onstageSlot
)
{
renderObject
.
remove
(
child
);
assert
(
child
is
RenderStack
);
renderObject
.
child
=
child
as
RenderStack
;
}
else
{
assert
(
slot
==
null
||
slot
is
Element
);
if
(
renderObject
.
child
==
child
)
{
renderObject
.
child
=
null
;
renderObject
.
insert
(
child
,
after:
slot
?.
renderObject
as
RenderBox
);
}
else
{
renderObject
.
move
(
child
,
after:
slot
?.
renderObject
as
RenderBox
);
}
}
return
child
;
}
RenderBox
get
_lastOnstageChild
=>
skipCount
==
super
.
childCount
?
null
:
lastChild
;
int
get
_onstageChildCount
=>
childCount
-
skipCount
;
@override
double
computeMinIntrinsicWidth
(
double
height
)
{
return
RenderStack
.
getIntrinsicDimension
(
_firstOnstageChild
,
(
RenderBox
child
)
=>
child
.
getMinIntrinsicWidth
(
height
));
void
removeChildRenderObject
(
RenderBox
child
)
{
if
(
renderObject
.
child
==
child
)
{
renderObject
.
child
=
null
;
}
else
{
renderObject
.
remove
(
child
);
}
}
@override
double
computeMaxIntrinsicWidth
(
double
height
)
{
return
RenderStack
.
getIntrinsicDimension
(
_firstOnstageChild
,
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicWidth
(
height
));
void
visitChildren
(
ElementVisitor
visitor
)
{
if
(
_onstage
!=
null
)
visitor
(
_onstage
);
for
(
final
Element
child
in
_offstage
)
{
if
(!
_forgottenOffstageChildren
.
contains
(
child
))
visitor
(
child
);
}
}
@override
double
computeMinIntrinsicHeight
(
double
width
)
{
return
RenderStack
.
getIntrinsicDimension
(
_firstOnstageChild
,
(
RenderBox
child
)
=>
child
.
getMinIntrinsicHeight
(
width
));
void
debugVisitOnstageChildren
(
ElementVisitor
visitor
)
{
if
(
_onstage
!=
null
)
visitor
(
_onstage
);
}
@override
double
computeMaxIntrinsicHeight
(
double
width
)
{
return
RenderStack
.
getIntrinsicDimension
(
_firstOnstageChild
,
(
RenderBox
child
)
=>
child
.
getMaxIntrinsicHeight
(
width
));
bool
forgetChild
(
Element
child
)
{
if
(
child
==
_onstage
)
{
_onstage
=
null
;
}
else
{
assert
(
_offstage
.
contains
(
child
));
assert
(!
_forgottenOffstageChildren
.
contains
(
child
));
_forgottenOffstageChildren
.
add
(
child
);
}
return
true
;
}
@override
double
computeDistanceToActualBaseline
(
TextBaseline
baseline
)
{
assert
(!
debugNeedsLayout
);
double
result
;
RenderBox
child
=
_firstOnstageChild
;
while
(
child
!=
null
)
{
assert
(!
child
.
debugNeedsLayout
);
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
double
candidate
=
child
.
getDistanceToActualBaseline
(
baseline
);
if
(
candidate
!=
null
)
{
candidate
+=
childParentData
.
offset
.
dy
;
if
(
result
!=
null
)
{
result
=
math
.
min
(
result
,
candidate
);
}
else
{
result
=
candidate
;
}
}
child
=
childParentData
.
nextSibling
;
void
mount
(
Element
parent
,
dynamic
newSlot
)
{
super
.
mount
(
parent
,
newSlot
);
_onstage
=
updateChild
(
_onstage
,
widget
.
onstage
,
_onstageSlot
);
_offstage
=
List
<
Element
>(
widget
.
offstage
.
length
);
Element
previousChild
;
for
(
int
i
=
0
;
i
<
_offstage
.
length
;
i
+=
1
)
{
final
Element
newChild
=
inflateWidget
(
widget
.
offstage
[
i
],
previousChild
);
_offstage
[
i
]
=
newChild
;
previousChild
=
newChild
;
}
return
result
;
}
@override
bool
get
sizedByParent
=>
true
;
@override
void
performResize
()
{
size
=
constraints
.
biggest
;
assert
(
size
.
isFinite
);
void
update
(
_Theatre
newWidget
)
{
super
.
update
(
newWidget
);
assert
(
widget
==
newWidget
);
_onstage
=
updateChild
(
_onstage
,
widget
.
onstage
,
_onstageSlot
);
_offstage
=
updateChildren
(
_offstage
,
widget
.
offstage
,
forgottenChildren:
_forgottenOffstageChildren
)
;
_forgottenOffstageChildren
.
clear
(
);
}
}
@override
void
performLayout
()
{
_hasVisualOverflow
=
false
;
if
(
_onstageChildCount
==
0
)
{
return
;
}
_resolve
();
assert
(
_resolvedAlignment
!=
null
);
// Same BoxConstraints as used by RenderStack for StackFit.expand.
final
BoxConstraints
nonPositionedConstraints
=
BoxConstraints
.
tight
(
constraints
.
biggest
);
RenderBox
child
=
_firstOnstageChild
;
while
(
child
!=
null
)
{
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
if
(!
childParentData
.
isPositioned
)
{
child
.
layout
(
nonPositionedConstraints
,
parentUsesSize:
true
);
childParentData
.
offset
=
_resolvedAlignment
.
alongOffset
(
size
-
child
.
size
as
Offset
);
}
else
{
_hasVisualOverflow
=
RenderStack
.
layoutPositionedChild
(
child
,
childParentData
,
size
,
_resolvedAlignment
)
||
_hasVisualOverflow
;
}
assert
(
child
.
parentData
==
childParentData
);
child
=
childParentData
.
nextSibling
;
}
}
// A render object which lays out and paints one subtree while keeping a list
// of other subtrees alive but not laid out or painted (the "zombie" children).
//
// The subtree that is laid out and painted must be a [RenderStack].
//
// This class uses [StackParentData] objects for its parent data so that the
// children of its primary subtree's stack can be moved to this object's list
// of zombie children without changing their parent data objects.
class
_RenderTheatre
extends
RenderBox
with
RenderObjectWithChildMixin
<
RenderStack
>,
RenderProxyBoxMixin
<
RenderStack
>,
ContainerRenderObjectMixin
<
RenderBox
,
StackParentData
>
{
@override
bool
hitTestChildren
(
BoxHitTestResult
result
,
{
Offset
position
})
{
RenderBox
child
=
_lastOnstageChild
;
for
(
int
i
=
0
;
i
<
_onstageChildCount
;
i
++)
{
assert
(
child
!=
null
);
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
final
bool
isHit
=
result
.
addWithPaintOffset
(
offset:
childParentData
.
offset
,
position:
position
,
hitTest:
(
BoxHitTestResult
result
,
Offset
transformed
)
{
assert
(
transformed
==
position
-
childParentData
.
offset
);
return
child
.
hitTest
(
result
,
position:
transformed
);
},
);
if
(
isHit
)
return
true
;
child
=
childParentData
.
previousSibling
;
}
return
false
;
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
StackParentData
)
child
.
parentData
=
StackParentData
();
}
@protected
void
paintStack
(
PaintingContext
context
,
Offset
offset
)
{
RenderBox
child
=
_firstOnstageChild
;
while
(
child
!=
null
)
{
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
context
.
paintChild
(
child
,
childParentData
.
offset
+
offset
);
child
=
childParentData
.
nextSibling
;
}
}
// Because both RenderObjectWithChildMixin and ContainerRenderObjectMixin
// define redepthChildren, visitChildren and debugDescribeChildren and don't
// call super, we have to define them again here to make sure the work of both
// is done.
//
// We chose to put ContainerRenderObjectMixin last in the inheritance chain so
// that we can call super to hit its more complex definitions of
// redepthChildren and visitChildren, and then duplicate the more trivial
// definition from RenderObjectWithChildMixin inline in our version here.
//
// This code duplication is suboptimal.
// TODO(ianh): Replace this with a better solution once https://github.com/dart-lang/sdk/issues/27100 is fixed
//
// For debugDescribeChildren we just roll our own because otherwise the line
// drawings won't really work as well.
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_hasVisualOverflow
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Offset
.
zero
&
size
,
paintStack
);
}
else
{
paintStack
(
context
,
offset
);
}
void
redepthChildren
()
{
if
(
child
!=
null
)
redepthChild
(
child
);
super
.
redepthChildren
();
}
@override
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
RenderBox
child
=
_firstOnstageChild
;
while
(
child
!=
null
)
{
void
visitChildren
(
RenderObjectVisitor
visitor
)
{
if
(
child
!=
null
)
visitor
(
child
);
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
child
=
childParentData
.
nextSibling
;
}
}
@override
Rect
describeApproximatePaintClip
(
RenderObject
child
)
=>
_hasVisualOverflow
?
Offset
.
zero
&
size
:
null
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
IntProperty
(
'skipCount'
,
skipCount
));
properties
.
add
(
EnumProperty
<
TextDirection
>(
'textDirection'
,
textDirection
));
super
.
visitChildren
(
visitor
);
}
@override
List
<
DiagnosticsNode
>
debugDescribeChildren
()
{
final
List
<
DiagnosticsNode
>
offstageChildren
=
<
DiagnosticsNode
>[];
final
List
<
DiagnosticsNode
>
onstageChildren
=
<
DiagnosticsNode
>[];
int
count
=
1
;
bool
onstage
=
false
;
RenderBox
child
=
firstChild
;
final
RenderBox
firstOnstageChild
=
_firstOnstageChild
;
while
(
child
!=
null
)
{
if
(
child
==
firstOnstageChild
)
{
onstage
=
true
;
count
=
1
;
}
final
List
<
DiagnosticsNode
>
children
=
<
DiagnosticsNode
>[
if
(
child
!=
null
)
child
.
toDiagnosticsNode
(
name:
'onstage'
),
];
if
(
onstage
)
{
onstageChildren
.
add
(
child
.
toDiagnosticsNode
(
name:
'onstage
$count
'
,
),
);
}
else
{
offstageChildren
.
add
(
if
(
firstChild
!=
null
)
{
RenderBox
child
=
firstChild
;
int
count
=
1
;
while
(
true
)
{
children
.
add
(
child
.
toDiagnosticsNode
(
name:
'offstage
$count
'
,
style:
DiagnosticsTreeStyle
.
offstage
,
),
);
if
(
child
==
lastChild
)
break
;
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
child
=
childParentData
.
nextSibling
;
count
+=
1
;
}
final
StackParentData
childParentData
=
child
.
parentData
as
StackParentData
;
child
=
childParentData
.
nextSibling
;
count
+=
1
;
}
return
<
DiagnosticsNode
>[
...
onstageChildren
,
if
(
offstageChildren
.
isNotEmpty
)
...
offstageChildren
else
}
else
{
children
.
add
(
DiagnosticsNode
.
message
(
'no offstage children'
,
style:
DiagnosticsTreeStyle
.
offstage
,
),
];
);
}
return
children
;
}
@override
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
child
!=
null
)
visitor
(
child
);
}
}
packages/flutter/test/cupertino/nav_bar_transition_test.dart
View file @
52c665d2
...
...
@@ -72,9 +72,12 @@ CupertinoPageScaffold scaffoldForNavBar(Widget navBar) {
}
Finder
flying
(
WidgetTester
tester
,
Finder
finder
)
{
final
ContainerRenderObjectMixin
<
RenderBox
,
StackParentData
>
theater
=
tester
.
renderObject
(
find
.
byType
(
Overlay
));
final
RenderObjectWithChildMixin
<
RenderStack
>
theater
=
tester
.
renderObject
(
find
.
byType
(
Overlay
));
final
RenderStack
theaterStack
=
theater
.
child
;
final
Finder
lastOverlayFinder
=
find
.
byElementPredicate
((
Element
element
)
{
return
element
is
RenderObjectElement
&&
element
.
renderObject
==
theater
.
lastChild
;
return
element
is
RenderObjectElement
&&
element
.
renderObject
==
theaterStack
.
lastChild
;
});
assert
(
...
...
packages/flutter/test/material/debug_test.dart
View file @
52c665d2
...
...
@@ -132,8 +132,8 @@ void main() {
' Offstage
\n
'
' _ModalScopeStatus
\n
'
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#969b7]
\n
'
'
TickerMode
\n
'
'
_OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#545d0]
\n
'
'
_OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#7a3ae]
\n
'
'
Stack
\n
'
' _Theatre
\n
'
' Overlay-[LabeledGlobalKey<OverlayState>#31a52]
\n
'
' _FocusMarker
\n
'
...
...
packages/flutter/test/material/stepper_test.dart
View file @
52c665d2
...
...
@@ -529,13 +529,12 @@ void main() {
// which will change depending on where the test is run.
expect
(
lines
.
length
,
greaterThan
(
7
));
expect
(
lines
.
take
(
9
).
join
(
'
\n
'
),
lines
.
take
(
8
).
join
(
'
\n
'
),
equalsIgnoringHashCodes
(
'══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞════════════════════════
\n
'
'The following assertion was thrown building Stepper(dirty,
\n
'
'dependencies: [TickerMode,
\n
'
'_LocalizationsScope-[GlobalKey#6b31b]], state:
\n
'
'_StepperState#1bf00):
\n
'
'dependencies: [_LocalizationsScope-[GlobalKey#00000]], state:
\n
'
'_StepperState#00000):
\n
'
'Steppers must not be nested.
\n
'
'The material specification advises that one should avoid
\n
'
'embedding steppers within steppers.
\n
'
...
...
packages/flutter/test/widgets/navigator_test.dart
View file @
52c665d2
...
...
@@ -1186,33 +1186,6 @@ void main() {
expect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'/A/B'
)),
findsNothing
);
// popped
expect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'/C'
)),
findsOneWidget
);
});
testWidgets
(
'Pushing opaque Route does not rebuild routes below'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/45797.
final
GlobalKey
<
NavigatorState
>
navigator
=
GlobalKey
<
NavigatorState
>();
final
Key
bottomRoute
=
UniqueKey
();
final
Key
topRoute
=
UniqueKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
navigatorKey:
navigator
,
routes:
<
String
,
WidgetBuilder
>{
'/'
:
(
BuildContext
context
)
=>
StatefulTestWidget
(
key:
bottomRoute
),
'/a'
:
(
BuildContext
context
)
=>
StatefulTestWidget
(
key:
topRoute
),
},
),
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottomRoute
)).
rebuildCount
,
1
);
navigator
.
currentState
.
pushNamed
(
'/a'
);
await
tester
.
pumpAndSettle
();
// Bottom route is offstage and did not rebuild.
expect
(
find
.
byKey
(
bottomRoute
),
findsNothing
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottomRoute
,
skipOffstage:
false
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
topRoute
)).
rebuildCount
,
1
);
});
}
class
NoAnimationPageRoute
extends
PageRouteBuilder
<
void
>
{
...
...
@@ -1226,20 +1199,3 @@ class NoAnimationPageRoute extends PageRouteBuilder<void> {
return
super
.
createAnimationController
()..
value
=
1.0
;
}
}
class
StatefulTestWidget
extends
StatefulWidget
{
const
StatefulTestWidget
({
Key
key
})
:
super
(
key:
key
);
@override
State
<
StatefulTestWidget
>
createState
()
=>
StatefulTestState
();
}
class
StatefulTestState
extends
State
<
StatefulTestWidget
>
{
int
rebuildCount
=
0
;
@override
Widget
build
(
BuildContext
context
)
{
rebuildCount
+=
1
;
return
Container
();
}
}
packages/flutter/test/widgets/overlay_test.dart
View file @
52c665d2
...
...
@@ -6,8 +6,6 @@ import 'package:flutter/foundation.dart';
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'semantics_tester.dart'
;
void
main
(
)
{
testWidgets
(
'OverflowEntries context contains Overlay'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
overlayKey
=
GlobalKey
();
...
...
@@ -27,9 +25,6 @@ void main() {
return
Container
();
},
),
OverlayEntry
(
builder:
(
BuildContext
context
)
=>
Container
(),
)
],
),
),
...
...
@@ -41,42 +36,36 @@ void main() {
expect
(
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
equalsIgnoringHashCodes
(
'_RenderTheatre#744c9
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ skipCount: 0
\n
'
' │ textDirection: ltr
\n
'
' │
\n
'
' ├─onstage 1: RenderLimitedBox#bb803
\n
'
' │ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' │ │ size)
\n
'
' │ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ │ size: Size(800.0, 600.0)
\n
'
' │ │ maxWidth: 0.0
\n
'
' │ │ maxHeight: 0.0
\n
'
' │ │
\n
'
' │ └─child: RenderConstrainedBox#62707
\n
'
' │ parentData: <none> (can use size)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ additionalConstraints: BoxConstraints(biggest)
\n
'
' │
\n
'
' ├─onstage 2: RenderLimitedBox#af5f1
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' ╎ │ size)
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ │ size: Size(800.0, 600.0)
\n
'
' ╎ │ maxWidth: 0.0
\n
'
' ╎ │ maxHeight: 0.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderConstrainedBox#69c48
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ size: Size(800.0, 600.0)
\n
'
' ╎ additionalConstraints: BoxConstraints(biggest)
\n
'
' ╎
\n
'
' └╌no offstage children
\n
'
'_RenderTheatre#f5cf2
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │
\n
'
' ├─onstage: RenderStack#39819
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' ╎ │ size)
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ │ size: Size(800.0, 600.0)
\n
'
' ╎ │ alignment: AlignmentDirectional.topStart
\n
'
' ╎ │ textDirection: ltr
\n
'
' ╎ │ fit: expand
\n
'
' ╎ │ overflow: clip
\n
'
' ╎ │
\n
'
' ╎ └─child 1: RenderLimitedBox#d1448
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' ╎ │ size)
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ │ size: Size(800.0, 600.0)
\n
'
' ╎ │ maxWidth: 0.0
\n
'
' ╎ │ maxHeight: 0.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderConstrainedBox#e8b87
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ size: Size(800.0, 600.0)
\n
'
' ╎ additionalConstraints: BoxConstraints(biggest)
\n
'
' ╎
\n
'
' └╌no offstage children
\n
'
),
);
});
...
...
@@ -114,52 +103,60 @@ void main() {
expect
(
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
equalsIgnoringHashCodes
(
'_RenderTheatre#
385b3
\n
'
'_RenderTheatre#
b22a8
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ skipCount: 2
\n
'
' │ textDirection: ltr
\n
'
' │
\n
'
' ├─onstage
1: RenderLimitedBox#0a77a
\n
'
' ├─onstage
: RenderStack#eab87
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' ╎ │ size)
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ │ size: Size(800.0, 600.0)
\n
'
' ╎ │ maxWidth: 0.0
\n
'
' ╎ │ maxHeight: 0.0
\n
'
' ╎ │ alignment: AlignmentDirectional.topStart
\n
'
' ╎ │ textDirection: ltr
\n
'
' ╎ │ fit: expand
\n
'
' ╎ │ overflow: clip
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderConstrainedBox#21f3a
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ size: Size(800.0, 600.0)
\n
'
' ╎ additionalConstraints: BoxConstraints(biggest)
\n
'
' ╎ └─child 1: RenderLimitedBox#ca15b
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0) (can use
\n
'
' ╎ │ size)
\n
'
' ╎ │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ │ size: Size(800.0, 600.0)
\n
'
' ╎ │ maxWidth: 0.0
\n
'
' ╎ │ maxHeight: 0.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderConstrainedBox#dffe5
\n
'
' ╎ parentData: <none> (can use size)
\n
'
' ╎ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' ╎ size: Size(800.0, 600.0)
\n
'
' ╎ additionalConstraints: BoxConstraints(biggest)
\n
'
' ╎
\n
'
' ╎╌offstage 1: RenderLimitedBox#
62c8c
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' ╎╌offstage 1: RenderLimitedBox#
b6f09
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' ╎ │ parentData: not positioned; offset=Offset(0.0, 0.0)
\n
'
' ╎ │ constraints: MISSING
\n
'
' ╎ │ size: MISSING
\n
'
' ╎ │ maxWidth: 0.0
\n
'
' ╎ │ maxHeight: 0.0
\n
'
' ╎ │
\n
'
' ╎ └─child: RenderConstrainedBox#
425fa
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' ╎ └─child: RenderConstrainedBox#
5a057
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' ╎ parentData: <none>
\n
'
' ╎ constraints: MISSING
\n
'
' ╎ size: MISSING
\n
'
' ╎ additionalConstraints: BoxConstraints(biggest)
\n
'
' ╎
\n
'
' └╌offstage 2: RenderLimitedBox#
03ae2
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' └╌offstage 2: RenderLimitedBox#
f689e
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' │ parentData: not positioned; offset=Offset(0.0, 0.0)
\n
'
' │ constraints: MISSING
\n
'
' │ size: MISSING
\n
'
' │ maxWidth: 0.0
\n
'
' │ maxHeight: 0.0
\n
'
' │
\n
'
' └─child: RenderConstrainedBox#
b4d48
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' └─child: RenderConstrainedBox#
c15f0
NEEDS-LAYOUT NEEDS-PAINT
\n
'
' parentData: <none>
\n
'
' constraints: MISSING
\n
'
' size: MISSING
\n
'
' additionalConstraints: BoxConstraints(biggest)
\n
'
,
' additionalConstraints: BoxConstraints(biggest)
\n
'
),
);
});
...
...
@@ -701,261 +698,4 @@ void main() {
expect
(
find
.
byKey
(
root
),
findsNothing
);
expect
(
find
.
byKey
(
top
),
findsOneWidget
);
});
testWidgets
(
'OverlayEntries do not rebuild when opaqueness changes'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/45797.
final
GlobalKey
<
OverlayState
>
overlayKey
=
GlobalKey
<
OverlayState
>();
final
Key
bottom
=
UniqueKey
();
final
Key
middle
=
UniqueKey
();
final
Key
top
=
UniqueKey
();
final
Widget
bottomWidget
=
StatefulTestWidget
(
key:
bottom
);
final
Widget
middleWidget
=
StatefulTestWidget
(
key:
middle
);
final
Widget
topWidget
=
StatefulTestWidget
(
key:
top
);
final
OverlayEntry
bottomEntry
=
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
bottomWidget
;
},
);
final
OverlayEntry
middleEntry
=
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
middleWidget
;
},
);
final
OverlayEntry
topEntry
=
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
topWidget
;
},
);
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
key:
overlayKey
,
initialEntries:
<
OverlayEntry
>[
bottomEntry
,
middleEntry
,
topEntry
,
],
),
),
);
// All widgets are onstage.
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottom
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
middle
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
top
)).
rebuildCount
,
1
);
middleEntry
.
opaque
=
true
;
await
tester
.
pump
();
// Bottom widget is offstage and did not rebuild.
expect
(
find
.
byKey
(
bottom
),
findsNothing
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottom
,
skipOffstage:
false
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
middle
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
top
)).
rebuildCount
,
1
);
});
testWidgets
(
'OverlayEntries do not rebuild when opaque entry is added'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/45797.
final
GlobalKey
<
OverlayState
>
overlayKey
=
GlobalKey
<
OverlayState
>();
final
Key
bottom
=
UniqueKey
();
final
Key
middle
=
UniqueKey
();
final
Key
top
=
UniqueKey
();
final
Widget
bottomWidget
=
StatefulTestWidget
(
key:
bottom
);
final
Widget
middleWidget
=
StatefulTestWidget
(
key:
middle
);
final
Widget
topWidget
=
StatefulTestWidget
(
key:
top
);
final
OverlayEntry
bottomEntry
=
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
bottomWidget
;
},
);
final
OverlayEntry
middleEntry
=
OverlayEntry
(
opaque:
true
,
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
middleWidget
;
},
);
final
OverlayEntry
topEntry
=
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
topWidget
;
},
);
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
key:
overlayKey
,
initialEntries:
<
OverlayEntry
>[
bottomEntry
,
topEntry
,
],
),
),
);
// Both widgets are onstage.
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottom
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
top
)).
rebuildCount
,
1
);
overlayKey
.
currentState
.
rearrange
(<
OverlayEntry
>[
bottomEntry
,
middleEntry
,
topEntry
,
]);
await
tester
.
pump
();
// Bottom widget is offstage and did not rebuild.
expect
(
find
.
byKey
(
bottom
),
findsNothing
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
bottom
,
skipOffstage:
false
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
middle
)).
rebuildCount
,
1
);
expect
(
tester
.
state
<
StatefulTestState
>(
find
.
byKey
(
top
)).
rebuildCount
,
1
);
});
testWidgets
(
'entries below opaque entries are ignored for hit testing'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
OverlayState
>
overlayKey
=
GlobalKey
<
OverlayState
>();
int
bottomTapCount
=
0
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
key:
overlayKey
,
initialEntries:
<
OverlayEntry
>[
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
bottomTapCount
++;
},
);
},
),
],
),
),
);
expect
(
bottomTapCount
,
0
);
await
tester
.
tap
(
find
.
byKey
(
overlayKey
));
expect
(
bottomTapCount
,
1
);
overlayKey
.
currentState
.
insert
(
OverlayEntry
(
maintainState:
true
,
opaque:
true
,
builder:
(
BuildContext
context
)
{
return
Container
();
},
));
await
tester
.
pump
();
// Bottom is offstage and does not receive tap events.
expect
(
find
.
byType
(
GestureDetector
),
findsNothing
);
expect
(
find
.
byType
(
GestureDetector
,
skipOffstage:
false
),
findsOneWidget
);
await
tester
.
tap
(
find
.
byKey
(
overlayKey
));
expect
(
bottomTapCount
,
1
);
int
topTapCount
=
0
;
overlayKey
.
currentState
.
insert
(
OverlayEntry
(
maintainState:
true
,
opaque:
true
,
builder:
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
topTapCount
++;
},
);
},
));
await
tester
.
pump
();
expect
(
topTapCount
,
0
);
await
tester
.
tap
(
find
.
byKey
(
overlayKey
));
expect
(
topTapCount
,
1
);
expect
(
bottomTapCount
,
1
);
});
testWidgets
(
'Semantics of entries below opaque entries are ignored'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
GlobalKey
<
OverlayState
>
overlayKey
=
GlobalKey
<
OverlayState
>();
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
key:
overlayKey
,
initialEntries:
<
OverlayEntry
>[
OverlayEntry
(
maintainState:
true
,
builder:
(
BuildContext
context
)
{
return
const
Text
(
'bottom'
);
},
),
OverlayEntry
(
maintainState:
true
,
opaque:
true
,
builder:
(
BuildContext
context
)
{
return
const
Text
(
'top'
);
},
),
],
),
),
);
expect
(
find
.
text
(
'bottom'
),
findsNothing
);
expect
(
find
.
text
(
'bottom'
,
skipOffstage:
false
),
findsOneWidget
);
expect
(
find
.
text
(
'top'
),
findsOneWidget
);
expect
(
semantics
,
includesNodeWith
(
label:
'top'
));
expect
(
semantics
,
isNot
(
includesNodeWith
(
label:
'bottom'
)));
semantics
.
dispose
();
});
testWidgets
(
'Can used Positioned within OverlayEntry'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
initialEntries:
<
OverlayEntry
>[
OverlayEntry
(
builder:
(
BuildContext
context
)
{
return
const
Positioned
(
left:
145
,
top:
123
,
child:
Text
(
'positioned child'
),
);
},
),
],
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'positioned child'
)),
const
Offset
(
145
,
123
));
});
}
class
StatefulTestWidget
extends
StatefulWidget
{
const
StatefulTestWidget
({
Key
key
})
:
super
(
key:
key
);
@override
State
<
StatefulTestWidget
>
createState
()
=>
StatefulTestState
();
}
class
StatefulTestState
extends
State
<
StatefulTestWidget
>
{
int
rebuildCount
=
0
;
@override
Widget
build
(
BuildContext
context
)
{
rebuildCount
+=
1
;
return
Container
();
}
}
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