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
e2cf2f0f
Unverified
Commit
e2cf2f0f
authored
Nov 22, 2019
by
Kate Lovett
Committed by
GitHub
Nov 22, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SliverOpacity (#44289)
parent
94270759
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
403 additions
and
36 deletions
+403
-36
flow.dart
packages/flutter/lib/src/rendering/flow.dart
+3
-4
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+5
-7
sliver.dart
packages/flutter/lib/src/rendering/sliver.dart
+152
-9
sliver.dart
packages/flutter/lib/src/widgets/sliver.dart
+102
-0
slivers_test.dart
packages/flutter/test/widgets/slivers_test.dart
+141
-16
No files found.
packages/flutter/lib/src/rendering/flow.dart
View file @
e2cf2f0f
...
...
@@ -2,8 +2,9 @@
// 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
'
dart:ui'
as
ui
show
Color
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'box.dart'
;
...
...
@@ -134,8 +135,6 @@ abstract class FlowDelegate {
String
toString
()
=>
'
$runtimeType
'
;
}
int
_getAlphaFromOpacity
(
double
opacity
)
=>
(
opacity
*
255
).
round
();
/// Parent data for use with [RenderFlow].
///
/// The [offset] property is ignored by [RenderFlow] and is always set to
...
...
@@ -343,7 +342,7 @@ class RenderFlow extends RenderBox
if
(
opacity
==
1.0
)
{
_paintingContext
.
pushTransform
(
needsCompositing
,
_paintingOffset
,
transform
,
painter
);
}
else
{
_paintingContext
.
pushOpacity
(
_paintingOffset
,
_
getAlphaFromOpacity
(
opacity
),
(
PaintingContext
context
,
Offset
offset
)
{
_paintingContext
.
pushOpacity
(
_paintingOffset
,
ui
.
Color
.
getAlphaFromOpacity
(
opacity
),
(
PaintingContext
context
,
Offset
offset
)
{
context
.
pushTransform
(
needsCompositing
,
offset
,
transform
,
painter
);
});
}
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
e2cf2f0f
...
...
@@ -4,7 +4,7 @@
import
'dart:async'
;
import
'dart:ui'
as
ui
show
ImageFilter
,
Gradient
,
Image
;
import
'dart:ui'
as
ui
show
ImageFilter
,
Gradient
,
Image
,
Color
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
...
...
@@ -713,8 +713,6 @@ class RenderIntrinsicHeight extends RenderProxyBox {
}
int
_getAlphaFromOpacity
(
double
opacity
)
=>
(
opacity
*
255
).
round
();
/// Makes its child partially transparent.
///
/// This class paints its child into an intermediate buffer and then blends the
...
...
@@ -737,7 +735,7 @@ class RenderOpacity extends RenderProxyBox {
assert
(
alwaysIncludeSemantics
!=
null
),
_opacity
=
opacity
,
_alwaysIncludeSemantics
=
alwaysIncludeSemantics
,
_alpha
=
_
getAlphaFromOpacity
(
opacity
),
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
opacity
),
super
(
child
);
@override
...
...
@@ -765,11 +763,11 @@ class RenderOpacity extends RenderProxyBox {
final
bool
didNeedCompositing
=
alwaysNeedsCompositing
;
final
bool
wasVisible
=
_alpha
!=
0
;
_opacity
=
value
;
_alpha
=
_
getAlphaFromOpacity
(
_opacity
);
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
_opacity
);
if
(
didNeedCompositing
!=
alwaysNeedsCompositing
)
markNeedsCompositingBitsUpdate
();
markNeedsPaint
();
if
(
wasVisible
!=
(
_alpha
!=
0
))
if
(
wasVisible
!=
(
_alpha
!=
0
)
&&
!
alwaysIncludeSemantics
)
markNeedsSemanticsUpdate
();
}
...
...
@@ -895,7 +893,7 @@ class RenderAnimatedOpacity extends RenderProxyBox {
void
_updateOpacity
()
{
final
int
oldAlpha
=
_alpha
;
_alpha
=
_getAlphaFromOpacity
(
_opacity
.
value
.
clamp
(
0.0
,
1.0
)
);
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
_opacity
.
value
);
if
(
oldAlpha
!=
_alpha
)
{
final
bool
didNeedCompositing
=
_currentlyNeedsCompositing
;
_currentlyNeedsCompositing
=
_alpha
>
0
&&
_alpha
<
255
;
...
...
packages/flutter/lib/src/rendering/sliver.dart
View file @
e2cf2f0f
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'dart:ui'
as
ui
show
Color
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
...
...
@@ -1818,6 +1819,156 @@ class RenderSliverToBoxAdapter extends RenderSliverSingleBoxAdapter {
}
}
/// Makes its sliver child partially transparent.
///
/// This class paints its sliver child into an intermediate buffer and then
/// blends the sliver child back into the scene, partially transparent.
///
/// For values of opacity other than 0.0 and 1.0, this class is relatively
/// expensive, because it requires painting the sliver child into an intermediate
/// buffer. For the value 0.0, the sliver child is simply not painted at all.
/// For the value 1.0, the sliver child is painted immediately without an
/// intermediate buffer.
class
RenderSliverOpacity
extends
RenderSliver
with
RenderObjectWithChildMixin
<
RenderSliver
>
{
/// Creates a partially transparent render object.
///
/// The [opacity] argument must be between 0.0 and 1.0, inclusive.
RenderSliverOpacity
({
double
opacity
=
1.0
,
bool
alwaysIncludeSemantics
=
false
,
RenderSliver
sliver
,
})
:
assert
(
opacity
!=
null
&&
opacity
>=
0.0
&&
opacity
<=
1.0
),
assert
(
alwaysIncludeSemantics
!=
null
),
_opacity
=
opacity
,
_alwaysIncludeSemantics
=
alwaysIncludeSemantics
,
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
opacity
)
{
child
=
sliver
;
}
@override
bool
get
alwaysNeedsCompositing
=>
child
!=
null
&&
(
_alpha
!=
0
&&
_alpha
!=
255
);
int
_alpha
;
/// The fraction to scale the child's alpha value.
///
/// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
/// (i.e. invisible).
///
/// The opacity must not be null.
///
/// Values 1.0 and 0.0 are painted with a fast path. Other values
/// require painting the child into an intermediate buffer, which is
/// expensive.
double
get
opacity
=>
_opacity
;
double
_opacity
;
set
opacity
(
double
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0.0
&&
value
<=
1.0
);
if
(
_opacity
==
value
)
return
;
final
bool
didNeedCompositing
=
alwaysNeedsCompositing
;
final
bool
wasVisible
=
_alpha
!=
0
;
_opacity
=
value
;
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
_opacity
);
if
(
didNeedCompositing
!=
alwaysNeedsCompositing
)
markNeedsCompositingBitsUpdate
();
markNeedsPaint
();
if
(
wasVisible
!=
(
_alpha
!=
0
)
&&
!
alwaysIncludeSemantics
)
markNeedsSemanticsUpdate
();
}
/// Whether child semantics are included regardless of the opacity.
///
/// If false, semantics are excluded when [opacity] is 0.0.
///
/// Defaults to false.
bool
get
alwaysIncludeSemantics
=>
_alwaysIncludeSemantics
;
bool
_alwaysIncludeSemantics
;
set
alwaysIncludeSemantics
(
bool
value
)
{
if
(
value
==
_alwaysIncludeSemantics
)
return
;
_alwaysIncludeSemantics
=
value
;
markNeedsSemanticsUpdate
();
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
SliverPhysicalParentData
)
child
.
parentData
=
SliverPhysicalParentData
();
}
@override
void
performLayout
()
{
assert
(
child
!=
null
);
child
.
layout
(
constraints
,
parentUsesSize:
true
);
geometry
=
child
.
geometry
;
}
@override
bool
hitTestChildren
(
SliverHitTestResult
result
,
{
double
mainAxisPosition
,
double
crossAxisPosition
})
{
return
child
!=
null
&&
child
.
geometry
.
hitTestExtent
>
0
&&
child
.
hitTest
(
result
,
mainAxisPosition:
mainAxisPosition
,
crossAxisPosition:
crossAxisPosition
,
);
}
@override
double
childMainAxisPosition
(
RenderSliver
child
)
{
assert
(
child
!=
null
);
assert
(
child
==
this
.
child
);
return
0.0
;
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
_paintWithOpacity
(
PaintingContext
context
,
Offset
offset
)
=>
context
.
paintChild
(
child
,
offset
);
if
(
child
!=
null
&&
child
.
geometry
.
visible
)
{
if
(
_alpha
==
0
)
{
// No need to keep the layer. We'll create a new one if necessary.
layer
=
null
;
return
;
}
if
(
_alpha
==
255
)
{
// No need to keep the layer. We'll create a new one if necessary.
layer
=
null
;
context
.
paintChild
(
child
,
offset
);
return
;
}
assert
(
needsCompositing
);
layer
=
context
.
pushOpacity
(
offset
,
_alpha
,
_paintWithOpacity
,
oldLayer:
layer
,
);
}
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
applyPaintTransform
(
transform
);
}
@override
void
visitChildrenForSemantics
(
RenderObjectVisitor
visitor
)
{
if
(
child
!=
null
&&
(
_alpha
!=
0
||
alwaysIncludeSemantics
))
visitor
(
child
);
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DoubleProperty
(
'opacity'
,
opacity
));
properties
.
add
(
FlagProperty
(
'alwaysIncludeSemantics'
,
value:
alwaysIncludeSemantics
,
ifTrue:
'alwaysIncludeSemantics'
,));
}
}
/// A render object that is invisible during hit testing.
///
/// When [ignoring] is true, this render object (and its subtree) is invisible
...
...
@@ -1928,14 +2079,6 @@ class RenderSliverIgnorePointer extends RenderSliver with RenderObjectWithChildM
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'ignoring'
,
ignoring
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'ignoringSemantics'
,
_effectiveIgnoringSemantics
,
description:
ignoringSemantics
==
null
?
'implicitly
$_effectiveIgnoringSemantics
'
:
null
,
),
);
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'ignoringSemantics'
,
_effectiveIgnoringSemantics
,
description:
ignoringSemantics
==
null
?
'implicitly
$_effectiveIgnoringSemantics
'
:
null
,),);
}
}
packages/flutter/lib/src/widgets/sliver.dart
View file @
e2cf2f0f
...
...
@@ -1625,6 +1625,108 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
}
}
/// A sliver widget that makes its sliver child partially transparent.
///
/// This class paints its sliver child into an intermediate buffer and then
/// blends the sliver back into the scene partially transparent.
///
/// For values of opacity other than 0.0 and 1.0, this class is relatively
/// expensive because it requires painting the sliver child into an intermediate
/// buffer. For the value 0.0, the sliver child is simply not painted at all.
/// For the value 1.0, the sliver child is painted immediately without an
/// intermediate buffer.
///
/// {@tool sample}
///
/// This example shows a [SliverList] when the `_visible` member field is true,
/// and hides it when it is false:
///
/// ```dart
/// bool _visible = true;
/// List<Widget> listItems = <Widget>[
/// Text('Now you see me,'),
/// Text('Now you don\'t!'),
/// ];
///
/// SliverOpacity(
/// opacity: _visible ? 1.0 : 0.0,
/// sliver: SliverList(
/// delegate: SliverChildListDelegate(listItems),
/// ),
/// )
/// ```
/// {@end-tool}
///
/// This is more efficient than adding and removing the sliver child widget
/// from the tree on demand.
///
/// See also:
///
/// * [Opacity], which can apply a uniform alpha effect to its child using the
/// RenderBox layout protocol.
/// * [AnimatedOpacity], which uses an animation internally to efficiently
/// animate [Opacity].
class
SliverOpacity
extends
SingleChildRenderObjectWidget
{
/// Creates a sliver that makes its sliver child partially transparent.
///
/// The [opacity] argument must not be null and must be between 0.0 and 1.0
/// (inclusive).
const
SliverOpacity
({
Key
key
,
@required
this
.
opacity
,
this
.
alwaysIncludeSemantics
=
false
,
Widget
sliver
,
})
:
assert
(
opacity
!=
null
&&
opacity
>=
0.0
&&
opacity
<=
1.0
),
assert
(
alwaysIncludeSemantics
!=
null
),
super
(
key:
key
,
child:
sliver
);
/// The fraction to scale the sliver child's alpha value.
///
/// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
/// (i.e. invisible).
///
/// The opacity must not be null.
///
/// Values 1.0 and 0.0 are painted with a fast path. Other values
/// require painting the sliver child into an intermediate buffer, which is
/// expensive.
final
double
opacity
;
/// Whether the semantic information of the sliver child is always included.
///
/// Defaults to false.
///
/// When true, regardless of the opacity settings, the sliver child semantic
/// information is exposed as if the widget were fully visible. This is
/// useful in cases where labels may be hidden during animations that
/// would otherwise contribute relevant semantics.
final
bool
alwaysIncludeSemantics
;
@override
RenderSliverOpacity
createRenderObject
(
BuildContext
context
)
{
return
RenderSliverOpacity
(
opacity:
opacity
,
alwaysIncludeSemantics:
alwaysIncludeSemantics
,
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
RenderSliverOpacity
renderObject
)
{
renderObject
..
opacity
=
opacity
..
alwaysIncludeSemantics
=
alwaysIncludeSemantics
;
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
double
>(
'opacity'
,
opacity
));
properties
.
add
(
FlagProperty
(
'alwaysIncludeSemantics'
,
value:
alwaysIncludeSemantics
,
ifTrue:
'alwaysIncludeSemantics'
,));
}
}
/// A sliver widget that is invisible during hit testing.
///
/// When [ignoring] is true, this widget (and its subtree) is invisible
...
...
packages/flutter/test/widgets/slivers_test.dart
View file @
e2cf2f0f
...
...
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import
'package:flutter/widgets.dart'
;
import
'package:flutter/rendering.dart'
;
import
'../rendering/mock_canvas.dart'
;
import
'semantics_tester.dart'
;
Future
<
void
>
test
(
WidgetTester
tester
,
double
offset
,
{
double
anchor
=
0.0
})
{
...
...
@@ -422,24 +423,148 @@ void main() {
expect
(
controller
.
offset
,
800.0
);
});
group
(
'SliverIgnorePointer - '
,
()
{
Widget
_boilerPlate
(
Widget
sliver
)
{
return
Localizations
(
locale:
const
Locale
(
'en'
,
'us'
),
delegates:
const
<
LocalizationsDelegate
<
dynamic
>>[
DefaultWidgetsLocalizations
.
delegate
,
DefaultMaterialLocalizations
.
delegate
,
],
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CustomScrollView
(
slivers:
<
Widget
>[
sliver
])
)
Widget
_boilerPlate
(
Widget
sliver
)
{
return
Localizations
(
locale:
const
Locale
(
'en'
,
'us'
),
delegates:
const
<
LocalizationsDelegate
<
dynamic
>>[
DefaultWidgetsLocalizations
.
delegate
,
DefaultMaterialLocalizations
.
delegate
,
],
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CustomScrollView
(
slivers:
<
Widget
>[
sliver
])
)
)
);
}
group
(
'SliverOpacity - '
,
()
{
testWidgets
(
'painting & semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
// Opacity 1.0: Semantics and painting
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
1.0
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
1
));
expect
(
find
.
byType
(
SliverOpacity
),
paints
..
paragraph
());
// Opacity 0.0: Nothing
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.0
,
)
);
}
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
0
));
expect
(
find
.
byType
(
SliverOpacity
),
paintsNothing
);
// Opacity 0.0 with semantics: Just semantics
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.0
,
alwaysIncludeSemantics:
true
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
1
));
expect
(
find
.
byType
(
SliverOpacity
),
paintsNothing
);
// Opacity 0.0 without semantics: Nothing
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.0
,
alwaysIncludeSemantics:
false
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
0
));
expect
(
find
.
byType
(
SliverOpacity
),
paintsNothing
);
// Opacity 0.1: Semantics and painting
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.1
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
1
));
expect
(
find
.
byType
(
SliverOpacity
),
paints
..
paragraph
());
// Opacity 0.1 without semantics: Still has semantics and painting
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.1
,
alwaysIncludeSemantics:
false
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
1
));
expect
(
find
.
byType
(
SliverOpacity
),
paints
..
paragraph
());
// Opacity 0.1 with semantics: Semantics and painting
await
tester
.
pumpWidget
(
_boilerPlate
(
const
SliverOpacity
(
sliver:
SliverToBoxAdapter
(
child:
Text
(
'a'
,
textDirection:
TextDirection
.
rtl
,
)
),
opacity:
0.1
,
alwaysIncludeSemantics:
true
,
),
));
expect
(
semantics
.
nodesWith
(
label:
'a'
),
hasLength
(
1
));
expect
(
find
.
byType
(
SliverOpacity
),
paints
..
paragraph
());
semantics
.
dispose
();
});
});
group
(
'SliverIgnorePointer - '
,
()
{
testWidgets
(
'ignores pointer events'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
List
<
String
>
events
=
<
String
>[];
...
...
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