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
3a1a2533
Unverified
Commit
3a1a2533
authored
Sep 20, 2022
by
Jonah Williams
Committed by
GitHub
Sep 20, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[framework] avoid compositing with visibility (#111844)
parent
1b583a83
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
185 additions
and
10 deletions
+185
-10
visibility.dart
packages/flutter/lib/src/widgets/visibility.dart
+138
-10
visibility_test.dart
packages/flutter/test/widgets/visibility_test.dart
+47
-0
No files found.
packages/flutter/lib/src/widgets/visibility.dart
View file @
3a1a2533
...
...
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/
foundation
.dart'
;
import
'package:flutter/
rendering
.dart'
;
import
'basic.dart'
;
import
'framework.dart'
;
...
...
@@ -168,7 +168,7 @@ class Visibility extends StatelessWidget {
/// [child] subtree is not trivial then it is significantly cheaper to not
/// even keep the state (see [maintainState]).
///
/// If this property is
true, [Opacity] is used instead of [Offstage]
.
/// If this property is
false, [Offstage] is used
.
///
/// If this property is false, then [maintainSemantics] and
/// [maintainInteractivity] must also be false.
...
...
@@ -222,9 +222,9 @@ class Visibility extends StatelessWidget {
child:
child
,
);
}
return
Opac
ity
(
opacity:
visible
?
1.0
:
0.0
,
alwaysInclude
Semantics:
maintainSemantics
,
return
_Visibil
ity
(
visible:
visible
,
maintain
Semantics:
maintainSemantics
,
child:
result
,
);
}
...
...
@@ -410,8 +410,7 @@ class SliverVisibility extends StatelessWidget {
/// [sliver] subtree is not trivial then it is significantly cheaper to not
/// even keep the state (see [maintainState]).
///
/// If this property is true, [SliverOpacity] is used instead of
/// [SliverOffstage].
/// If this property is false, [SliverOffstage] is used.
///
/// If this property is false, then [maintainSemantics] and
/// [maintainInteractivity] must also be false.
...
...
@@ -460,9 +459,9 @@ class SliverVisibility extends StatelessWidget {
ignoringSemantics:
!
visible
&&
!
maintainSemantics
,
);
}
return
SliverOpac
ity
(
opacity:
visible
?
1.0
:
0.0
,
alwaysInclude
Semantics:
maintainSemantics
,
return
_SliverVisibil
ity
(
visible:
visible
,
maintain
Semantics:
maintainSemantics
,
sliver:
result
,
);
}
...
...
@@ -495,3 +494,132 @@ class SliverVisibility extends StatelessWidget {
properties
.
add
(
FlagProperty
(
'maintainInteractivity'
,
value:
maintainInteractivity
,
ifFalse:
'maintainInteractivity'
));
}
}
// A widget that conditionally hides its child, but without the forced compositing of `Opacity`.
//
// A fully opaque `Opacity` widget is required to leave its opacity layer in the layer tree. This
// forces all parent render objects to also composite, which can break a simple scene into many
// different layers. This can be significantly more expensive, so the issue is avoided by a
// specialized render object that does not ever force compositing.
class
_Visibility
extends
SingleChildRenderObjectWidget
{
const
_Visibility
({
required
this
.
visible
,
required
this
.
maintainSemantics
,
super
.
child
});
final
bool
visible
;
final
bool
maintainSemantics
;
@override
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
_RenderVisibility
(
visible
,
maintainSemantics
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
covariant
RenderObject
renderObject
)
{
(
renderObject
as
_RenderVisibility
)
..
visible
=
visible
..
maintainSemantics
=
maintainSemantics
;
}
}
class
_RenderVisibility
extends
RenderProxyBox
{
_RenderVisibility
(
this
.
_visible
,
this
.
_maintainSemantics
);
bool
get
visible
=>
_visible
;
bool
_visible
;
set
visible
(
bool
value
)
{
if
(
value
==
visible
)
{
return
;
}
_visible
=
value
;
markNeedsPaint
();
}
bool
get
maintainSemantics
=>
_maintainSemantics
;
bool
_maintainSemantics
;
set
maintainSemantics
(
bool
value
)
{
if
(
value
==
maintainSemantics
)
{
return
;
}
_maintainSemantics
=
value
;
markNeedsSemanticsUpdate
();
}
@override
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
maintainSemantics
||
visible
)
{
super
.
visitChildrenForSemantics
(
visitor
);
}
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(!
visible
)
{
return
;
}
super
.
paint
(
context
,
offset
);
}
}
// A widget that conditionally hides its child, but without the forced compositing of `SliverOpacity`.
//
// A fully opaque `SliverOpacity` widget is required to leave its opacity layer in the layer tree.
// This forces all parent render objects to also composite, which can break a simple scene into many
// different layers. This can be significantly more expensive, so the issue is avoided by a
// specialized render object that does not ever force compositing.
class
_SliverVisibility
extends
SingleChildRenderObjectWidget
{
const
_SliverVisibility
({
required
this
.
visible
,
required
this
.
maintainSemantics
,
Widget
?
sliver
})
:
super
(
child:
sliver
);
final
bool
visible
;
final
bool
maintainSemantics
;
@override
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
_RenderSliverVisibility
(
visible
,
maintainSemantics
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
covariant
RenderObject
renderObject
)
{
(
renderObject
as
_RenderSliverVisibility
)
..
visible
=
visible
..
maintainSemantics
=
maintainSemantics
;
}
}
class
_RenderSliverVisibility
extends
RenderProxySliver
{
_RenderSliverVisibility
(
this
.
_visible
,
this
.
_maintainSemantics
);
bool
get
visible
=>
_visible
;
bool
_visible
;
set
visible
(
bool
value
)
{
if
(
value
==
visible
)
{
return
;
}
_visible
=
value
;
markNeedsPaint
();
}
bool
get
maintainSemantics
=>
_maintainSemantics
;
bool
_maintainSemantics
;
set
maintainSemantics
(
bool
value
)
{
if
(
value
==
maintainSemantics
)
{
return
;
}
_maintainSemantics
=
value
;
markNeedsSemanticsUpdate
();
}
@override
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
maintainSemantics
||
visible
)
{
super
.
visitChildrenForSemantics
(
visitor
);
}
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(!
visible
)
{
return
;
}
super
.
paint
(
context
,
offset
);
}
}
packages/flutter/test/widgets/visibility_test.dart
View file @
3a1a2533
...
...
@@ -2,6 +2,7 @@
// 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/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
@@ -424,4 +425,50 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'Visibility does not force compositing when visible and maintain*'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Visibility
(
maintainSize:
true
,
maintainAnimation:
true
,
maintainState:
true
,
child:
Text
(
'hello'
,
textDirection:
TextDirection
.
ltr
),
),
);
// Root transform from the tester and then the picture created by the text.
expect
(
tester
.
layers
,
hasLength
(
2
));
expect
(
tester
.
layers
,
isNot
(
contains
(
isA
<
OpacityLayer
>())));
expect
(
tester
.
layers
.
last
,
isA
<
PictureLayer
>());
});
testWidgets
(
'SliverVisibility does not force compositing when visible and maintain*'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CustomScrollView
(
slivers:
<
Widget
>[
SliverVisibility
(
maintainSize:
true
,
maintainAnimation:
true
,
maintainState:
true
,
sliver:
SliverList
(
delegate:
SliverChildListDelegate
.
fixed
(
addRepaintBoundaries:
false
,
<
Widget
>[
Text
(
'hello'
),
],
),
))
]
),
),
);
// This requires a lot more layers due to including sliver lists which do manage additional
// offset layers. Just trust me this is one fewer layers than before...
expect
(
tester
.
layers
,
hasLength
(
6
));
expect
(
tester
.
layers
,
isNot
(
contains
(
isA
<
OpacityLayer
>())));
expect
(
tester
.
layers
.
last
,
isA
<
PictureLayer
>());
});
}
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