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
d54cdf9e
Unverified
Commit
d54cdf9e
authored
May 17, 2022
by
Dan Field
Committed by
GitHub
May 17, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a mechanism to observe layer tree composition. (#103378)
parent
c62a7d41
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
442 additions
and
4 deletions
+442
-4
layer.dart
packages/flutter/lib/src/rendering/layer.dart
+144
-3
object.dart
packages/flutter/lib/src/rendering/object.dart
+16
-1
layers_test.dart
packages/flutter/test/rendering/layers_test.dart
+249
-0
object_test.dart
packages/flutter/test/rendering/object_test.dart
+33
-0
No files found.
packages/flutter/lib/src/rendering/layer.dart
View file @
d54cdf9e
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/rendering/object.dart
View file @
d54cdf9e
...
...
@@ -12,7 +12,6 @@ import 'package:flutter/painting.dart';
import
'package:flutter/semantics.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'binding.dart'
;
import
'debug.dart'
;
import
'layer.dart'
;
...
...
@@ -326,6 +325,22 @@ class PaintingContext extends ClipContext {
_containerLayer
.
append
(
_currentLayer
!);
}
/// Adds a [CompositionCallback] for the current [ContainerLayer] used by this
/// context.
///
/// Composition callbacks are called whenever the layer tree containing the
/// current layer of this painting context gets composited, or when it gets
/// detached and will not be rendered again. This happens regardless of
/// whether the layer is added via retained rendering or not.
///
/// {@macro flutter.rendering.Layer.compositionCallbacks}
///
/// See also:
/// * [Layer.addCompositionCallback].
VoidCallback
addCompositionCallback
(
CompositionCallback
callback
)
{
return
_containerLayer
.
addCompositionCallback
(
callback
);
}
/// Stop recording to a canvas if recording has started.
///
/// Do not call this function directly: functions in this class will call
...
...
packages/flutter/test/rendering/layers_test.dart
View file @
d54cdf9e
...
...
@@ -716,6 +716,255 @@ void main() {
layer
.
addToScene
(
builder
);
expect
(
layer
.
engineLayer
,
null
);
});
test
(
'Layers describe clip bounds'
,
()
{
ContainerLayer
layer
=
ContainerLayer
();
expect
(
layer
.
describeClipBounds
(),
null
);
const
Rect
bounds
=
Rect
.
fromLTRB
(
10
,
10
,
20
,
20
);
final
RRect
rbounds
=
RRect
.
fromRectXY
(
bounds
,
2
,
2
);
layer
=
ClipRectLayer
(
clipRect:
bounds
);
expect
(
layer
.
describeClipBounds
(),
bounds
);
layer
=
ClipRRectLayer
(
clipRRect:
rbounds
);
expect
(
layer
.
describeClipBounds
(),
rbounds
.
outerRect
);
layer
=
ClipPathLayer
(
clipPath:
Path
()..
addRect
(
bounds
));
expect
(
layer
.
describeClipBounds
(),
bounds
);
});
test
(
'Subtree has composition callbacks'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
final
List
<
VoidCallback
>
cancellationCallbacks
=
<
VoidCallback
>[];
cancellationCallbacks
.
add
(
root
.
addCompositionCallback
((
_
)
{}));
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
b1
.
subtreeHasCompositionCallbacks
,
false
);
cancellationCallbacks
.
add
(
b1
.
addCompositionCallback
((
_
)
{}));
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
b1
.
subtreeHasCompositionCallbacks
,
true
);
cancellationCallbacks
.
removeAt
(
0
)();
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
b1
.
subtreeHasCompositionCallbacks
,
true
);
cancellationCallbacks
.
removeAt
(
0
)();
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
b1
.
subtreeHasCompositionCallbacks
,
false
);
});
test
(
'Subtree has composition callbacks - removeChild'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
expect
(
b1
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
b1
.
addCompositionCallback
((
_
)
{
});
expect
(
b1
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
b1
.
remove
();
expect
(
b1
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a2
.
subtreeHasCompositionCallbacks
,
false
);
});
test
(
'No callback if removed'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
// Add and immediately remove the callback.
b1
.
addCompositionCallback
((
Layer
layer
)
{
fail
(
'Should not have called back'
);
})();
root
.
buildScene
(
SceneBuilder
()).
dispose
();
});
test
(
'Observe layer tree composition - not retained'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
bool
compositedB1
=
false
;
b1
.
addCompositionCallback
((
Layer
layer
)
{
expect
(
layer
,
b1
);
compositedB1
=
true
;
});
expect
(
compositedB1
,
false
);
root
.
buildScene
(
SceneBuilder
()).
dispose
();
expect
(
compositedB1
,
true
);
});
test
(
'Observe layer tree composition - retained'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
// Actually build the retained layer so that the engine sees it as real and
// reusable.
SceneBuilder
builder
=
SceneBuilder
();
b1
.
engineLayer
=
builder
.
pushOffset
(
0
,
0
);
builder
.
build
().
dispose
();
builder
=
SceneBuilder
();
// Force the layer to appear clean and have an engine layer for retained
// rendering.
expect
(
b1
.
engineLayer
,
isNotNull
);
b1
.
debugMarkClean
();
expect
(
b1
.
debugSubtreeNeedsAddToScene
,
false
);
bool
compositedB1
=
false
;
b1
.
addCompositionCallback
((
Layer
layer
)
{
expect
(
layer
,
b1
);
compositedB1
=
true
;
});
expect
(
compositedB1
,
false
);
root
.
buildScene
(
builder
).
dispose
();
expect
(
compositedB1
,
true
);
});
test
(
'Observe layer tree composition - asserts on mutation'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
bool
compositedB1
=
false
;
b1
.
addCompositionCallback
((
Layer
layer
)
{
expect
(
layer
,
b1
);
expect
(()
=>
layer
.
remove
(),
throwsAssertionError
);
expect
(()
=>
layer
.
dispose
(),
throwsAssertionError
);
expect
(()
=>
layer
.
markNeedsAddToScene
(),
throwsAssertionError
);
expect
(()
=>
layer
.
debugMarkClean
(),
throwsAssertionError
);
expect
(()
=>
layer
.
updateSubtreeNeedsAddToScene
(),
throwsAssertionError
);
expect
(()
=>
layer
.
dropChild
(
ContainerLayer
()),
throwsAssertionError
);
expect
(()
=>
layer
.
adoptChild
(
ContainerLayer
()),
throwsAssertionError
);
expect
(()
=>
(
layer
as
ContainerLayer
).
append
(
ContainerLayer
()),
throwsAssertionError
);
expect
(()
=>
layer
.
engineLayer
=
null
,
throwsAssertionError
);
compositedB1
=
true
;
});
expect
(
compositedB1
,
false
);
root
.
buildScene
(
SceneBuilder
()).
dispose
();
expect
(
compositedB1
,
true
);
});
test
(
'Observe layer tree composition - detach triggers callback'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
final
ContainerLayer
a2
=
ContainerLayer
();
final
ContainerLayer
b1
=
ContainerLayer
();
root
.
append
(
a1
);
root
.
append
(
a2
);
a1
.
append
(
b1
);
bool
compositedB1
=
false
;
b1
.
addCompositionCallback
((
Layer
layer
)
{
expect
(
layer
,
b1
);
compositedB1
=
true
;
});
root
.
attach
(
Object
());
expect
(
compositedB1
,
false
);
root
.
detach
();
expect
(
compositedB1
,
true
);
});
test
(
'Observe layer tree composition - observer count correctly maintained'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
ContainerLayer
a1
=
ContainerLayer
();
root
.
append
(
a1
);
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
final
VoidCallback
remover1
=
a1
.
addCompositionCallback
((
_
)
{
});
final
VoidCallback
remover2
=
a1
.
addCompositionCallback
((
_
)
{
});
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
true
);
remover1
();
expect
(
root
.
subtreeHasCompositionCallbacks
,
true
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
true
);
remover2
();
expect
(
root
.
subtreeHasCompositionCallbacks
,
false
);
expect
(
a1
.
subtreeHasCompositionCallbacks
,
false
);
});
}
class
FakeEngineLayer
extends
Fake
implements
EngineLayer
{
...
...
packages/flutter/test/rendering/object_test.dart
View file @
d54cdf9e
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
...
...
@@ -246,8 +248,39 @@ void main() {
renderObject
.
dispose
();
expect
(
renderObject
.
debugLayer
,
null
);
});
test
(
'Add composition callback works'
,
()
{
final
ContainerLayer
root
=
ContainerLayer
();
final
PaintingContext
context
=
PaintingContext
(
root
,
Rect
.
zero
);
bool
calledBack
=
false
;
final
TestObservingRenderObject
object
=
TestObservingRenderObject
((
Layer
layer
)
{
expect
(
layer
,
root
);
calledBack
=
true
;
});
expect
(
calledBack
,
false
);
object
.
paint
(
context
,
Offset
.
zero
);
expect
(
calledBack
,
false
);
root
.
buildScene
(
ui
.
SceneBuilder
()).
dispose
();
expect
(
calledBack
,
true
);
});
}
class
TestObservingRenderObject
extends
RenderBox
{
TestObservingRenderObject
(
this
.
callback
);
final
CompositionCallback
callback
;
@override
bool
get
sizedByParent
=>
true
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
context
.
addCompositionCallback
(
callback
);
}
}
// Tests the create-update cycle by pumping two frames. The first frame has no
// prior layer and forces the painting context to create a new one. The second
// frame reuses the layer painted on the first frame.
...
...
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