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
e7ab3b07
Unverified
Commit
e7ab3b07
authored
Mar 02, 2023
by
LongCatIsLooong
Committed by
GitHub
Mar 02, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
`OverlayPortal` (#105335)
`OverlayPortal`
parent
60de2aa9
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
3066 additions
and
132 deletions
+3066
-132
overlay_portal.0.dart
examples/api/lib/widgets/overlay/overlay_portal.0.dart
+59
-0
overlay_portal.0_test.dart
examples/api/test/widgets/overlay/overlay_portal.0_test.dart
+44
-0
material.dart
packages/flutter/lib/src/material/material.dart
+65
-24
object.dart
packages/flutter/lib/src/rendering/object.dart
+26
-9
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+1
-0
overlay.dart
packages/flutter/lib/src/widgets/overlay.dart
+1220
-96
debug_test.dart
packages/flutter/test/material/debug_test.dart
+2
-1
ink_paint_test.dart
packages/flutter/test/material/ink_paint_test.dart
+60
-0
overlay_portal_test.dart
packages/flutter/test/widgets/overlay_portal_test.dart
+1587
-0
overlay_test.dart
packages/flutter/test/widgets/overlay_test.dart
+2
-2
No files found.
examples/api/lib/widgets/overlay/overlay_portal.0.dart
0 → 100644
View file @
e7ab3b07
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for OverlayPortal
import
'package:flutter/material.dart'
;
void
main
(
)
=>
runApp
(
const
MyApp
());
class
MyApp
extends
StatelessWidget
{
const
MyApp
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
MaterialApp
(
title:
'Flutter Code Sample'
,
home:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'OverlayPortal Example'
)),
body:
const
Center
(
child:
ClickableTooltipWidget
()),
),
);
}
}
class
ClickableTooltipWidget
extends
StatefulWidget
{
const
ClickableTooltipWidget
({
super
.
key
});
@override
State
<
StatefulWidget
>
createState
()
=>
ClickableTooltipWidgetState
();
}
class
ClickableTooltipWidgetState
extends
State
<
ClickableTooltipWidget
>
{
final
OverlayPortalController
_tooltipController
=
OverlayPortalController
();
@override
Widget
build
(
BuildContext
context
)
{
return
TextButton
(
onPressed:
_tooltipController
.
toggle
,
child:
DefaultTextStyle
(
style:
DefaultTextStyle
.
of
(
context
).
style
.
copyWith
(
fontSize:
50
),
child:
OverlayPortal
(
controller:
_tooltipController
,
overlayChildBuilder:
(
BuildContext
context
)
{
return
const
Positioned
(
right:
50
,
bottom:
50
,
child:
ColoredBox
(
color:
Colors
.
amberAccent
,
child:
Text
(
'tooltip'
),
),
);
},
child:
const
Text
(
'Press to show/hide tooltip'
),
),
),
);
}
}
examples/api/test/widgets/overlay/overlay_portal.0_test.dart
0 → 100644
View file @
e7ab3b07
// Copyright 2014 The Flutter 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/rendering.dart'
;
import
'package:flutter_api_samples/widgets/overlay/overlay_portal.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
const
String
tooltipText
=
'tooltip'
;
testWidgets
(
'Tooltip is shown on press'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MyApp
());
expect
(
find
.
text
(
tooltipText
),
findsNothing
);
await
tester
.
tap
(
find
.
byType
(
example
.
ClickableTooltipWidget
));
await
tester
.
pump
();
expect
(
find
.
text
(
tooltipText
),
findsOneWidget
);
await
tester
.
tap
(
find
.
byType
(
example
.
ClickableTooltipWidget
));
await
tester
.
pump
();
expect
(
find
.
text
(
tooltipText
),
findsNothing
);
});
testWidgets
(
'Tooltip is shown at the right location'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MyApp
());
await
tester
.
tap
(
find
.
byType
(
example
.
ClickableTooltipWidget
));
await
tester
.
pump
();
final
Size
canvasSize
=
tester
.
getSize
(
find
.
byType
(
example
.
MyApp
));
expect
(
tester
.
getBottomRight
(
find
.
text
(
tooltipText
)),
canvasSize
-
const
Size
(
50
,
50
),
);
});
testWidgets
(
'Tooltip is shown with the right font size'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MyApp
());
await
tester
.
tap
(
find
.
byType
(
example
.
ClickableTooltipWidget
));
await
tester
.
pump
();
final
TextSpan
textSpan
=
tester
.
renderObject
<
RenderParagraph
>(
find
.
text
(
tooltipText
)).
text
as
TextSpan
;
expect
(
textSpan
.
style
?.
fontSize
,
50
);
});
}
packages/flutter/lib/src/material/material.dart
View file @
e7ab3b07
...
@@ -641,7 +641,7 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
...
@@ -641,7 +641,7 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
}
}
void
_didChangeLayout
()
{
void
_didChangeLayout
()
{
if
(
_inkFeatures
!=
null
&&
_inkFeatures
!.
isNotEmpty
)
{
if
(
_inkFeatures
?.
isNotEmpty
??
false
)
{
markNeedsPaint
();
markNeedsPaint
();
}
}
}
}
...
@@ -651,16 +651,18 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
...
@@ -651,16 +651,18 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
@override
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_inkFeatures
!=
null
&&
_inkFeatures
!.
isNotEmpty
)
{
final
List
<
InkFeature
>?
inkFeatures
=
_inkFeatures
;
if
(
inkFeatures
!=
null
&&
inkFeatures
.
isNotEmpty
)
{
final
Canvas
canvas
=
context
.
canvas
;
final
Canvas
canvas
=
context
.
canvas
;
canvas
.
save
();
canvas
.
save
();
canvas
.
translate
(
offset
.
dx
,
offset
.
dy
);
canvas
.
translate
(
offset
.
dx
,
offset
.
dy
);
canvas
.
clipRect
(
Offset
.
zero
&
size
);
canvas
.
clipRect
(
Offset
.
zero
&
size
);
for
(
final
InkFeature
inkFeature
in
_inkFeatures
!
)
{
for
(
final
InkFeature
inkFeature
in
inkFeatures
)
{
inkFeature
.
_paint
(
canvas
);
inkFeature
.
_paint
(
canvas
);
}
}
canvas
.
restore
();
canvas
.
restore
();
}
}
assert
(
inkFeatures
==
_inkFeatures
);
super
.
paint
(
context
,
offset
);
super
.
paint
(
context
,
offset
);
}
}
}
}
...
@@ -740,32 +742,71 @@ abstract class InkFeature {
...
@@ -740,32 +742,71 @@ abstract class InkFeature {
onRemoved
?.
call
();
onRemoved
?.
call
();
}
}
// Returns the paint transform that allows `fromRenderObject` to perform paint
// in `toRenderObject`'s coordinate space.
//
// Returns null if either `fromRenderObject` or `toRenderObject` is not in the
// same render tree, or either of them is in an offscreen subtree (see
// RenderObject.paintsChild).
static
Matrix4
?
_getPaintTransform
(
RenderObject
fromRenderObject
,
RenderObject
toRenderObject
,
)
{
// The paths to fromRenderObject and toRenderObject's common ancestor.
final
List
<
RenderObject
>
fromPath
=
<
RenderObject
>[
fromRenderObject
];
final
List
<
RenderObject
>
toPath
=
<
RenderObject
>[
toRenderObject
];
RenderObject
from
=
fromRenderObject
;
RenderObject
to
=
toRenderObject
;
while
(!
identical
(
from
,
to
))
{
final
int
fromDepth
=
from
.
depth
;
final
int
toDepth
=
to
.
depth
;
if
(
fromDepth
>=
toDepth
)
{
final
AbstractNode
?
fromParent
=
from
.
parent
;
// Return early if the 2 render objects are not in the same render tree,
// or either of them is offscreen and thus won't get painted.
if
(
fromParent
is
!
RenderObject
||
!
fromParent
.
paintsChild
(
from
))
{
return
null
;
}
fromPath
.
add
(
fromParent
);
from
=
fromParent
;
}
if
(
fromDepth
<=
toDepth
)
{
final
AbstractNode
?
toParent
=
to
.
parent
;
if
(
toParent
is
!
RenderObject
||
!
toParent
.
paintsChild
(
to
))
{
return
null
;
}
toPath
.
add
(
toParent
);
to
=
toParent
;
}
}
assert
(
identical
(
from
,
to
));
final
Matrix4
transform
=
Matrix4
.
identity
();
final
Matrix4
inverseTransform
=
Matrix4
.
identity
();
for
(
int
index
=
toPath
.
length
-
1
;
index
>
0
;
index
-=
1
)
{
toPath
[
index
].
applyPaintTransform
(
toPath
[
index
-
1
],
transform
);
}
for
(
int
index
=
fromPath
.
length
-
1
;
index
>
0
;
index
-=
1
)
{
fromPath
[
index
].
applyPaintTransform
(
fromPath
[
index
-
1
],
inverseTransform
);
}
final
double
det
=
inverseTransform
.
invert
();
return
det
!=
0
?
(
inverseTransform
..
multiply
(
transform
))
:
null
;
}
void
_paint
(
Canvas
canvas
)
{
void
_paint
(
Canvas
canvas
)
{
assert
(
referenceBox
.
attached
);
assert
(
referenceBox
.
attached
);
assert
(!
_debugDisposed
);
assert
(!
_debugDisposed
);
// find the chain of renderers from us to the feature's referenceBox
final
List
<
RenderObject
>
descendants
=
<
RenderObject
>[
referenceBox
];
RenderObject
node
=
referenceBox
;
while
(
node
!=
_controller
)
{
final
RenderObject
childNode
=
node
;
node
=
node
.
parent
!
as
RenderObject
;
if
(!
node
.
paintsChild
(
childNode
))
{
// Some node between the reference box and this would skip painting on
// the reference box, so bail out early and avoid unnecessary painting.
// Some cases where this can happen are the reference box being
// offstage, in a fully transparent opacity node, or in a keep alive
// bucket.
return
;
}
descendants
.
add
(
node
);
}
// determine the transform that gets our coordinate system to be like theirs
// determine the transform that gets our coordinate system to be like theirs
final
Matrix4
transform
=
Matrix4
.
identity
();
final
Matrix4
?
transform
=
_getPaintTransform
(
_controller
,
referenceBox
);
assert
(
descendants
.
length
>=
2
);
if
(
transform
!=
null
)
{
for
(
int
index
=
descendants
.
length
-
1
;
index
>
0
;
index
-=
1
)
{
paintFeature
(
canvas
,
transform
);
descendants
[
index
].
applyPaintTransform
(
descendants
[
index
-
1
],
transform
);
}
}
paintFeature
(
canvas
,
transform
);
}
}
/// Override this method to paint the ink feature.
/// Override this method to paint the ink feature.
...
...
packages/flutter/lib/src/rendering/object.dart
View file @
e7ab3b07
...
@@ -1486,7 +1486,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
...
@@ -1486,7 +1486,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// in other cases will lead to an inconsistent tree and probably cause crashes.
/// in other cases will lead to an inconsistent tree and probably cause crashes.
@override
@override
void
adoptChild
(
RenderObject
child
)
{
void
adoptChild
(
RenderObject
child
)
{
assert
(
_debugCanPerformMutations
);
setupParentData
(
child
);
setupParentData
(
child
);
markNeedsLayout
();
markNeedsLayout
();
markNeedsCompositingBitsUpdate
();
markNeedsCompositingBitsUpdate
();
...
@@ -1500,7 +1499,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
...
@@ -1500,7 +1499,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// in other cases will lead to an inconsistent tree and probably cause crashes.
/// in other cases will lead to an inconsistent tree and probably cause crashes.
@override
@override
void
dropChild
(
RenderObject
child
)
{
void
dropChild
(
RenderObject
child
)
{
assert
(
_debugCanPerformMutations
);
assert
(
child
.
parentData
!=
null
);
assert
(
child
.
parentData
!=
null
);
child
.
_cleanRelayoutBoundary
();
child
.
_cleanRelayoutBoundary
();
child
.
parentData
!.
detach
();
child
.
parentData
!.
detach
();
...
@@ -1643,7 +1641,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
...
@@ -1643,7 +1641,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
}
}
if
(!
activeLayoutRoot
.
_debugMutationsLocked
)
{
if
(!
activeLayoutRoot
.
_debugMutationsLocked
)
{
final
AbstractNode
?
p
=
activeLayoutRoot
.
p
arent
;
final
AbstractNode
?
p
=
activeLayoutRoot
.
debugLayoutP
arent
;
activeLayoutRoot
=
p
is
RenderObject
?
p
:
null
;
activeLayoutRoot
=
p
is
RenderObject
?
p
:
null
;
}
else
{
}
else
{
// activeLayoutRoot found.
// activeLayoutRoot found.
...
@@ -1722,6 +1720,29 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
...
@@ -1722,6 +1720,29 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
return
result
;
return
result
;
}
}
/// The [RenderObject] that's expected to call [layout] on this [RenderObject]
/// in its [performLayout] implementation.
///
/// This method is used to implement an assert that ensures the render subtree
/// actively performing layout can not get accidently mutated. It's only
/// implemented in debug mode and always returns null in release mode.
///
/// The default implementation returns [parent] and overriding is rarely
/// needed. A [RenderObject] subclass that expects its
/// [RenderObject.performLayout] to be called from a different [RenderObject]
/// that's not its [parent] should override this property to return the actual
/// layout parent.
@protected
RenderObject
?
get
debugLayoutParent
{
RenderObject
?
layoutParent
;
assert
(()
{
final
AbstractNode
?
parent
=
this
.
parent
;
layoutParent
=
parent
is
RenderObject
?
?
parent
:
null
;
return
true
;
}());
return
layoutParent
;
}
@override
@override
PipelineOwner
?
get
owner
=>
super
.
owner
as
PipelineOwner
?;
PipelineOwner
?
get
owner
=>
super
.
owner
as
PipelineOwner
?;
...
@@ -3636,17 +3657,13 @@ mixin RenderObjectWithChildMixin<ChildType extends RenderObject> on RenderObject
...
@@ -3636,17 +3657,13 @@ mixin RenderObjectWithChildMixin<ChildType extends RenderObject> on RenderObject
@override
@override
void
attach
(
PipelineOwner
owner
)
{
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
super
.
attach
(
owner
);
if
(
_child
!=
null
)
{
_child
?.
attach
(
owner
);
_child
!.
attach
(
owner
);
}
}
}
@override
@override
void
detach
()
{
void
detach
()
{
super
.
detach
();
super
.
detach
();
if
(
_child
!=
null
)
{
_child
?.
detach
();
_child
!.
detach
();
}
}
}
@override
@override
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
e7ab3b07
...
@@ -4701,6 +4701,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
...
@@ -4701,6 +4701,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
performRebuild
();
performRebuild
();
}
finally
{
}
finally
{
assert
(()
{
assert
(()
{
owner
!.
_debugElementWasRebuilt
(
this
);
assert
(
owner
!.
_debugCurrentBuildTarget
==
this
);
assert
(
owner
!.
_debugCurrentBuildTarget
==
this
);
owner
!.
_debugCurrentBuildTarget
=
debugPreviousBuildTarget
;
owner
!.
_debugCurrentBuildTarget
=
debugPreviousBuildTarget
;
return
true
;
return
true
;
...
...
packages/flutter/lib/src/widgets/overlay.dart
View file @
e7ab3b07
This diff is collapsed.
Click to expand it.
packages/flutter/test/material/debug_test.dart
View file @
e7ab3b07
...
@@ -152,10 +152,11 @@ void main() {
...
@@ -152,10 +152,11 @@ void main() {
' AnimatedBuilder
\n
'
' AnimatedBuilder
\n
'
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#00000]
\n
'
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#00000]
\n
'
' Semantics
\n
'
' Semantics
\n
'
' _RenderTheaterMarker
\n
'
' _EffectiveTickerMode
\n
'
' _EffectiveTickerMode
\n
'
' TickerMode
\n
'
' TickerMode
\n
'
' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#00000]
\n
'
' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#00000]
\n
'
' _Theat
re
\n
'
' _Theat
er
\n
'
' Overlay-[LabeledGlobalKey<OverlayState>#00000]
\n
'
' Overlay-[LabeledGlobalKey<OverlayState>#00000]
\n
'
' UnmanagedRestorationScope
\n
'
' UnmanagedRestorationScope
\n
'
' _FocusInheritedScope
\n
'
' _FocusInheritedScope
\n
'
...
...
packages/flutter/test/material/ink_paint_test.dart
View file @
e7ab3b07
...
@@ -454,6 +454,66 @@ void main() {
...
@@ -454,6 +454,66 @@ void main() {
}));
}));
});
});
testWidgets
(
'The InkWell widget on OverlayPortal does not throw'
,
(
WidgetTester
tester
)
async
{
final
OverlayPortalController
controller
=
OverlayPortalController
();
controller
.
show
();
await
tester
.
pumpWidget
(
Center
(
child:
RepaintBoundary
(
child:
SizedBox
.
square
(
dimension:
200
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Overlay
(
initialEntries:
<
OverlayEntry
>[
OverlayEntry
(
builder:
(
BuildContext
context
)
{
return
Center
(
child:
SizedBox
.
square
(
dimension:
100
,
// The material partially overlaps the overlayChild.
// This is to verify that the `overlayChild`'s ink
// features aren't clipped by it.
child:
Material
(
color:
Colors
.
black
,
child:
OverlayPortal
(
controller:
controller
,
overlayChildBuilder:
(
BuildContext
context
)
{
return
Positioned
(
right:
0
,
bottom:
0
,
child:
InkWell
(
splashColor:
Colors
.
red
,
onTap:
()
{},
child:
const
SizedBox
.
square
(
dimension:
100
),
),
);
},
),
),
),
);
},
),
],
),
),
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
InkWell
)));
addTearDown
(()
async
{
await
gesture
.
up
();
});
await
tester
.
pump
();
// start gesture
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
expect
(
tester
.
takeException
(),
isNull
);
});
testWidgets
(
'Custom rectCallback renders an ink splash from its center'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Custom rectCallback renders an ink splash from its center'
,
(
WidgetTester
tester
)
async
{
const
Color
splashColor
=
Color
(
0xff00ff00
);
const
Color
splashColor
=
Color
(
0xff00ff00
);
...
...
packages/flutter/test/widgets/overlay_portal_test.dart
0 → 100644
View file @
e7ab3b07
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/overlay_test.dart
View file @
e7ab3b07
...
@@ -41,7 +41,7 @@ void main() {
...
@@ -41,7 +41,7 @@ void main() {
expect
(
expect
(
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
equalsIgnoringHashCodes
(
equalsIgnoringHashCodes
(
'_RenderTheat
re
#744c9
\n
'
'_RenderTheat
er
#744c9
\n
'
' │ parentData: <none>
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
...
@@ -114,7 +114,7 @@ void main() {
...
@@ -114,7 +114,7 @@ void main() {
expect
(
expect
(
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
theater
.
toStringDeep
(
minLevel:
DiagnosticLevel
.
info
),
equalsIgnoringHashCodes
(
equalsIgnoringHashCodes
(
'_RenderTheat
re
#385b3
\n
'
'_RenderTheat
er
#385b3
\n
'
' │ parentData: <none>
\n
'
' │ parentData: <none>
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ constraints: BoxConstraints(w=800.0, h=600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
' │ size: Size(800.0, 600.0)
\n
'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment