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
a84bb4eb
Unverified
Commit
a84bb4eb
authored
Jun 01, 2021
by
Dwayne Slater
Committed by
GitHub
Jun 01, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add ability to specify Image widget opacity as an animation (#83379)
parent
59f6cc7a
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
194 additions
and
39 deletions
+194
-39
decoration_image.dart
packages/flutter/lib/src/painting/decoration_image.dart
+4
-0
image.dart
packages/flutter/lib/src/rendering/image.dart
+33
-0
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+11
-0
fade_in_image.dart
packages/flutter/lib/src/widgets/fade_in_image.dart
+57
-30
image.dart
packages/flutter/lib/src/widgets/image.dart
+21
-0
fade_in_image_test.dart
packages/flutter/test/widgets/fade_in_image_test.dart
+4
-9
image_test.dart
packages/flutter/test/widgets/image_test.dart
+64
-0
No files found.
packages/flutter/lib/src/painting/decoration_image.dart
View file @
a84bb4eb
...
@@ -360,6 +360,8 @@ void debugFlushLastFrameImageSizeInfo() {
...
@@ -360,6 +360,8 @@ void debugFlushLastFrameImageSizeInfo() {
///
///
/// * `scale`: The number of image pixels for each logical pixel.
/// * `scale`: The number of image pixels for each logical pixel.
///
///
/// * `opacity`: The opacity to paint the image onto the canvas with.
///
/// * `colorFilter`: If non-null, the color filter to apply when painting the
/// * `colorFilter`: If non-null, the color filter to apply when painting the
/// image.
/// image.
///
///
...
@@ -420,6 +422,7 @@ void paintImage({
...
@@ -420,6 +422,7 @@ void paintImage({
required
ui
.
Image
image
,
required
ui
.
Image
image
,
String
?
debugImageLabel
,
String
?
debugImageLabel
,
double
scale
=
1.0
,
double
scale
=
1.0
,
double
opacity
=
1.0
,
ColorFilter
?
colorFilter
,
ColorFilter
?
colorFilter
,
BoxFit
?
fit
,
BoxFit
?
fit
,
Alignment
alignment
=
Alignment
.
center
,
Alignment
alignment
=
Alignment
.
center
,
...
@@ -473,6 +476,7 @@ void paintImage({
...
@@ -473,6 +476,7 @@ void paintImage({
final
Paint
paint
=
Paint
()..
isAntiAlias
=
isAntiAlias
;
final
Paint
paint
=
Paint
()..
isAntiAlias
=
isAntiAlias
;
if
(
colorFilter
!=
null
)
if
(
colorFilter
!=
null
)
paint
.
colorFilter
=
colorFilter
;
paint
.
colorFilter
=
colorFilter
;
paint
.
color
=
Color
.
fromRGBO
(
0
,
0
,
0
,
opacity
);
paint
.
filterQuality
=
filterQuality
;
paint
.
filterQuality
=
filterQuality
;
paint
.
invertColors
=
invertColors
;
paint
.
invertColors
=
invertColors
;
final
double
halfWidthDelta
=
(
outputSize
.
width
-
destinationSize
.
width
)
/
2.0
;
final
double
halfWidthDelta
=
(
outputSize
.
width
-
destinationSize
.
width
)
/
2.0
;
...
...
packages/flutter/lib/src/rendering/image.dart
View file @
a84bb4eb
...
@@ -4,6 +4,8 @@
...
@@ -4,6 +4,8 @@
import
'dart:ui'
as
ui
show
Image
;
import
'dart:ui'
as
ui
show
Image
;
import
'package:flutter/animation.dart'
;
import
'box.dart'
;
import
'box.dart'
;
import
'object.dart'
;
import
'object.dart'
;
...
@@ -31,6 +33,7 @@ class RenderImage extends RenderBox {
...
@@ -31,6 +33,7 @@ class RenderImage extends RenderBox {
double
?
height
,
double
?
height
,
double
scale
=
1.0
,
double
scale
=
1.0
,
Color
?
color
,
Color
?
color
,
Animation
<
double
>?
opacity
,
BlendMode
?
colorBlendMode
,
BlendMode
?
colorBlendMode
,
BoxFit
?
fit
,
BoxFit
?
fit
,
AlignmentGeometry
alignment
=
Alignment
.
center
,
AlignmentGeometry
alignment
=
Alignment
.
center
,
...
@@ -52,6 +55,7 @@ class RenderImage extends RenderBox {
...
@@ -52,6 +55,7 @@ class RenderImage extends RenderBox {
_height
=
height
,
_height
=
height
,
_scale
=
scale
,
_scale
=
scale
,
_color
=
color
,
_color
=
color
,
_opacity
=
opacity
,
_colorBlendMode
=
colorBlendMode
,
_colorBlendMode
=
colorBlendMode
,
_fit
=
fit
,
_fit
=
fit
,
_alignment
=
alignment
,
_alignment
=
alignment
,
...
@@ -163,6 +167,21 @@ class RenderImage extends RenderBox {
...
@@ -163,6 +167,21 @@ class RenderImage extends RenderBox {
markNeedsPaint
();
markNeedsPaint
();
}
}
/// If non-null, the value from the [Animation] is multiplied with the opacity
/// of each image pixel before painting onto the canvas.
Animation
<
double
>?
get
opacity
=>
_opacity
;
Animation
<
double
>?
_opacity
;
set
opacity
(
Animation
<
double
>?
value
)
{
if
(
value
==
_opacity
)
return
;
if
(
attached
)
_opacity
?.
removeListener
(
markNeedsPaint
);
_opacity
=
value
;
if
(
attached
)
value
?.
addListener
(
markNeedsPaint
);
}
/// Used to set the filterQuality of the image
/// Used to set the filterQuality of the image
/// Use the [FilterQuality.low] quality setting to scale the image, which corresponds to
/// Use the [FilterQuality.low] quality setting to scale the image, which corresponds to
/// bilinear interpolation, rather than the default [FilterQuality.none] which corresponds
/// bilinear interpolation, rather than the default [FilterQuality.none] which corresponds
...
@@ -381,6 +400,18 @@ class RenderImage extends RenderBox {
...
@@ -381,6 +400,18 @@ class RenderImage extends RenderBox {
size
=
_sizeForConstraints
(
constraints
);
size
=
_sizeForConstraints
(
constraints
);
}
}
@override
void
attach
(
covariant
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
_opacity
?.
addListener
(
markNeedsPaint
);
}
@override
void
detach
()
{
_opacity
?.
removeListener
(
markNeedsPaint
);
super
.
detach
();
}
@override
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_image
==
null
)
if
(
_image
==
null
)
...
@@ -394,6 +425,7 @@ class RenderImage extends RenderBox {
...
@@ -394,6 +425,7 @@ class RenderImage extends RenderBox {
image:
_image
!,
image:
_image
!,
debugImageLabel:
debugImageLabel
,
debugImageLabel:
debugImageLabel
,
scale:
_scale
,
scale:
_scale
,
opacity:
_opacity
?.
value
??
1.0
,
colorFilter:
_colorFilter
,
colorFilter:
_colorFilter
,
fit:
_fit
,
fit:
_fit
,
alignment:
_resolvedAlignment
!,
alignment:
_resolvedAlignment
!,
...
@@ -414,6 +446,7 @@ class RenderImage extends RenderBox {
...
@@ -414,6 +446,7 @@ class RenderImage extends RenderBox {
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'scale'
,
scale
,
defaultValue:
1.0
));
properties
.
add
(
DoubleProperty
(
'scale'
,
scale
,
defaultValue:
1.0
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
Animation
<
double
>?>(
'opacity'
,
opacity
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
a84bb4eb
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
import
'dart:ui'
as
ui
show
Image
,
ImageFilter
,
TextHeightBehavior
;
import
'dart:ui'
as
ui
show
Image
,
ImageFilter
,
TextHeightBehavior
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
...
@@ -5915,6 +5916,7 @@ class RawImage extends LeafRenderObjectWidget {
...
@@ -5915,6 +5916,7 @@ class RawImage extends LeafRenderObjectWidget {
this
.
height
,
this
.
height
,
this
.
scale
=
1.0
,
this
.
scale
=
1.0
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -5961,6 +5963,13 @@ class RawImage extends LeafRenderObjectWidget {
...
@@ -5961,6 +5963,13 @@ class RawImage extends LeafRenderObjectWidget {
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
final
Color
?
color
;
final
Color
?
color
;
/// If non-null, the value from the [Animation] is multiplied with the opacity
/// of each image pixel before painting onto the canvas.
///
/// This is more efficient than using [FadeTransition] to change the opacity
/// of an image.
final
Animation
<
double
>?
opacity
;
/// Used to set the filterQuality of the image
/// Used to set the filterQuality of the image
/// Use the "low" quality setting to scale the image, which corresponds to
/// Use the "low" quality setting to scale the image, which corresponds to
/// bilinear interpolation, rather than the default "none" which corresponds
/// bilinear interpolation, rather than the default "none" which corresponds
...
@@ -6070,6 +6079,7 @@ class RawImage extends LeafRenderObjectWidget {
...
@@ -6070,6 +6079,7 @@ class RawImage extends LeafRenderObjectWidget {
height:
height
,
height:
height
,
scale:
scale
,
scale:
scale
,
color:
color
,
color:
color
,
opacity:
opacity
,
colorBlendMode:
colorBlendMode
,
colorBlendMode:
colorBlendMode
,
fit:
fit
,
fit:
fit
,
alignment:
alignment
,
alignment:
alignment
,
...
@@ -6122,6 +6132,7 @@ class RawImage extends LeafRenderObjectWidget {
...
@@ -6122,6 +6132,7 @@ class RawImage extends LeafRenderObjectWidget {
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'scale'
,
scale
,
defaultValue:
1.0
));
properties
.
add
(
DoubleProperty
(
'scale'
,
scale
,
defaultValue:
1.0
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
Animation
<
double
>?>(
'opacity'
,
opacity
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
...
...
packages/flutter/lib/src/widgets/fade_in_image.dart
View file @
a84bb4eb
...
@@ -11,7 +11,6 @@ import 'basic.dart';
...
@@ -11,7 +11,6 @@ import 'basic.dart';
import
'framework.dart'
;
import
'framework.dart'
;
import
'image.dart'
;
import
'image.dart'
;
import
'implicit_animations.dart'
;
import
'implicit_animations.dart'
;
import
'transitions.dart'
;
// Examples can assume:
// Examples can assume:
// late Uint8List bytes;
// late Uint8List bytes;
...
@@ -62,7 +61,7 @@ import 'transitions.dart';
...
@@ -62,7 +61,7 @@ import 'transitions.dart';
/// )
/// )
/// ```
/// ```
/// {@end-tool}
/// {@end-tool}
class
FadeInImage
extends
State
less
Widget
{
class
FadeInImage
extends
State
ful
Widget
{
/// Creates a widget that displays a [placeholder] while an [image] is loading,
/// Creates a widget that displays a [placeholder] while an [image] is loading,
/// then fades-out the placeholder and fades-in the image.
/// then fades-out the placeholder and fades-in the image.
///
///
...
@@ -356,22 +355,42 @@ class FadeInImage extends StatelessWidget {
...
@@ -356,22 +355,42 @@ class FadeInImage extends StatelessWidget {
/// once the image has loaded.
/// once the image has loaded.
final
String
?
imageSemanticLabel
;
final
String
?
imageSemanticLabel
;
@override
State
<
FadeInImage
>
createState
()
=>
_FadeInImageState
();
}
class
_FadeInImageState
extends
State
<
FadeInImage
>
{
static
const
Animation
<
double
>
_kOpaqueAnimation
=
AlwaysStoppedAnimation
<
double
>(
1.0
);
// These ProxyAnimations are changed to the fade in animation by
// [_AnimatedFadeOutFadeInState]. Otherwise these animations are reset to
// their defaults by [_resetAnimations].
final
ProxyAnimation
_imageAnimation
=
ProxyAnimation
(
_kOpaqueAnimation
);
final
ProxyAnimation
_placeholderAnimation
=
ProxyAnimation
(
_kOpaqueAnimation
);
void
_resetAnimations
()
{
_imageAnimation
.
parent
=
_kOpaqueAnimation
;
_placeholderAnimation
.
parent
=
_kOpaqueAnimation
;
}
Image
_image
({
Image
_image
({
required
ImageProvider
image
,
required
ImageProvider
image
,
ImageErrorWidgetBuilder
?
errorBuilder
,
ImageErrorWidgetBuilder
?
errorBuilder
,
ImageFrameBuilder
?
frameBuilder
,
ImageFrameBuilder
?
frameBuilder
,
required
Animation
<
double
>
opacity
,
})
{
})
{
assert
(
image
!=
null
);
assert
(
image
!=
null
);
return
Image
(
return
Image
(
image:
image
,
image:
image
,
errorBuilder:
errorBuilder
,
errorBuilder:
errorBuilder
,
frameBuilder:
frameBuilder
,
frameBuilder:
frameBuilder
,
width:
width
,
opacity:
opacity
,
height:
height
,
width:
widget
.
width
,
fit:
fit
,
height:
widget
.
height
,
alignment:
alignment
,
fit:
widget
.
fit
,
repeat:
repeat
,
alignment:
widget
.
alignment
,
matchTextDirection:
matchTextDirection
,
repeat:
widget
.
repeat
,
matchTextDirection:
widget
.
matchTextDirection
,
gaplessPlayback:
true
,
gaplessPlayback:
true
,
excludeFromSemantics:
true
,
excludeFromSemantics:
true
,
);
);
...
@@ -380,28 +399,37 @@ class FadeInImage extends StatelessWidget {
...
@@ -380,28 +399,37 @@ class FadeInImage extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
Widget
result
=
_image
(
Widget
result
=
_image
(
image:
image
,
image:
widget
.
image
,
errorBuilder:
imageErrorBuilder
,
errorBuilder:
widget
.
imageErrorBuilder
,
opacity:
_imageAnimation
,
frameBuilder:
(
BuildContext
context
,
Widget
child
,
int
?
frame
,
bool
wasSynchronouslyLoaded
)
{
frameBuilder:
(
BuildContext
context
,
Widget
child
,
int
?
frame
,
bool
wasSynchronouslyLoaded
)
{
if
(
wasSynchronouslyLoaded
)
if
(
wasSynchronouslyLoaded
)
{
_resetAnimations
();
return
child
;
return
child
;
}
return
_AnimatedFadeOutFadeIn
(
return
_AnimatedFadeOutFadeIn
(
target:
child
,
target:
child
,
placeholder:
_image
(
image:
placeholder
,
errorBuilder:
placeholderErrorBuilder
),
targetProxyAnimation:
_imageAnimation
,
placeholder:
_image
(
image:
widget
.
placeholder
,
errorBuilder:
widget
.
placeholderErrorBuilder
,
opacity:
_placeholderAnimation
,
),
placeholderProxyAnimation:
_placeholderAnimation
,
isTargetLoaded:
frame
!=
null
,
isTargetLoaded:
frame
!=
null
,
fadeInDuration:
fadeInDuration
,
fadeInDuration:
widget
.
fadeInDuration
,
fadeOutDuration:
fadeOutDuration
,
fadeOutDuration:
widget
.
fadeOutDuration
,
fadeInCurve:
fadeInCurve
,
fadeInCurve:
widget
.
fadeInCurve
,
fadeOutCurve:
fadeOutCurve
,
fadeOutCurve:
widget
.
fadeOutCurve
,
);
);
},
},
);
);
if
(!
excludeFromSemantics
)
{
if
(!
widget
.
excludeFromSemantics
)
{
result
=
Semantics
(
result
=
Semantics
(
container:
imageSemanticLabel
!=
null
,
container:
widget
.
imageSemanticLabel
!=
null
,
image:
true
,
image:
true
,
label:
imageSemanticLabel
??
''
,
label:
widget
.
imageSemanticLabel
??
''
,
child:
result
,
child:
result
,
);
);
}
}
...
@@ -414,7 +442,9 @@ class _AnimatedFadeOutFadeIn extends ImplicitlyAnimatedWidget {
...
@@ -414,7 +442,9 @@ class _AnimatedFadeOutFadeIn extends ImplicitlyAnimatedWidget {
const
_AnimatedFadeOutFadeIn
({
const
_AnimatedFadeOutFadeIn
({
Key
?
key
,
Key
?
key
,
required
this
.
target
,
required
this
.
target
,
required
this
.
targetProxyAnimation
,
required
this
.
placeholder
,
required
this
.
placeholder
,
required
this
.
placeholderProxyAnimation
,
required
this
.
isTargetLoaded
,
required
this
.
isTargetLoaded
,
required
this
.
fadeOutDuration
,
required
this
.
fadeOutDuration
,
required
this
.
fadeOutCurve
,
required
this
.
fadeOutCurve
,
...
@@ -430,7 +460,9 @@ class _AnimatedFadeOutFadeIn extends ImplicitlyAnimatedWidget {
...
@@ -430,7 +460,9 @@ class _AnimatedFadeOutFadeIn extends ImplicitlyAnimatedWidget {
super
(
key:
key
,
duration:
fadeInDuration
+
fadeOutDuration
);
super
(
key:
key
,
duration:
fadeInDuration
+
fadeOutDuration
);
final
Widget
target
;
final
Widget
target
;
final
ProxyAnimation
targetProxyAnimation
;
final
Widget
placeholder
;
final
Widget
placeholder
;
final
ProxyAnimation
placeholderProxyAnimation
;
final
bool
isTargetLoaded
;
final
bool
isTargetLoaded
;
final
Duration
fadeInDuration
;
final
Duration
fadeInDuration
;
final
Duration
fadeOutDuration
;
final
Duration
fadeOutDuration
;
...
@@ -494,6 +526,9 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
...
@@ -494,6 +526,9 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
// for the full animation when the new target image becomes ready.
// for the full animation when the new target image becomes ready.
controller
.
value
=
controller
.
upperBound
;
controller
.
value
=
controller
.
upperBound
;
}
}
widget
.
targetProxyAnimation
.
parent
=
_targetOpacityAnimation
;
widget
.
placeholderProxyAnimation
.
parent
=
_placeholderOpacityAnimation
;
}
}
bool
_isValid
(
Tween
<
double
>
tween
)
{
bool
_isValid
(
Tween
<
double
>
tween
)
{
...
@@ -502,13 +537,8 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
...
@@ -502,13 +537,8 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
Widget
target
=
FadeTransition
(
opacity:
_targetOpacityAnimation
!,
child:
widget
.
target
,
);
if
(
_placeholderOpacityAnimation
!.
isCompleted
)
{
if
(
_placeholderOpacityAnimation
!.
isCompleted
)
{
return
target
;
return
widget
.
target
;
}
}
return
Stack
(
return
Stack
(
...
@@ -518,11 +548,8 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
...
@@ -518,11 +548,8 @@ class _AnimatedFadeOutFadeInState extends ImplicitlyAnimatedWidgetState<_Animate
// but it allows the Stack to avoid a call to Directionality.of()
// but it allows the Stack to avoid a call to Directionality.of()
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
children:
<
Widget
>[
target
,
widget
.
target
,
FadeTransition
(
widget
.
placeholder
,
opacity:
_placeholderOpacityAnimation
!,
child:
widget
.
placeholder
,
),
],
],
);
);
}
}
...
...
packages/flutter/lib/src/widgets/image.dart
View file @
a84bb4eb
...
@@ -330,6 +330,7 @@ class Image extends StatefulWidget {
...
@@ -330,6 +330,7 @@ class Image extends StatefulWidget {
this
.
width
,
this
.
width
,
this
.
height
,
this
.
height
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -389,6 +390,7 @@ class Image extends StatefulWidget {
...
@@ -389,6 +390,7 @@ class Image extends StatefulWidget {
this
.
width
,
this
.
width
,
this
.
height
,
this
.
height
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -451,6 +453,7 @@ class Image extends StatefulWidget {
...
@@ -451,6 +453,7 @@ class Image extends StatefulWidget {
this
.
width
,
this
.
width
,
this
.
height
,
this
.
height
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -612,6 +615,7 @@ class Image extends StatefulWidget {
...
@@ -612,6 +615,7 @@ class Image extends StatefulWidget {
this
.
width
,
this
.
width
,
this
.
height
,
this
.
height
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -681,6 +685,7 @@ class Image extends StatefulWidget {
...
@@ -681,6 +685,7 @@ class Image extends StatefulWidget {
this
.
width
,
this
.
width
,
this
.
height
,
this
.
height
,
this
.
color
,
this
.
color
,
this
.
opacity
,
this
.
colorBlendMode
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
fit
,
this
.
alignment
=
Alignment
.
center
,
this
.
alignment
=
Alignment
.
center
,
...
@@ -922,6 +927,20 @@ class Image extends StatefulWidget {
...
@@ -922,6 +927,20 @@ class Image extends StatefulWidget {
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
final
Color
?
color
;
final
Color
?
color
;
/// If non-null, the value from the [Animation] is multiplied with the opacity
/// of each image pixel before painting onto the canvas.
///
/// This is more efficient than using [FadeTransition] to change the opacity
/// of an image, since this avoids creating a new composited layer. Composited
/// layers may double memory usage as the image is painted onto an offscreen
/// render target.
///
/// See also:
///
/// * [AlwaysStoppedAnimation], which allows you to create an [Animation]
/// from a single opacity value.
final
Animation
<
double
>?
opacity
;
/// The rendering quality of the image.
/// The rendering quality of the image.
///
///
/// If the image is of a high quality and its pixels are perfectly aligned
/// If the image is of a high quality and its pixels are perfectly aligned
...
@@ -1071,6 +1090,7 @@ class Image extends StatefulWidget {
...
@@ -1071,6 +1090,7 @@ class Image extends StatefulWidget {
properties
.
add
(
DoubleProperty
(
'width'
,
width
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'width'
,
width
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'height'
,
height
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
Animation
<
double
>?>(
'opacity'
,
opacity
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
AlignmentGeometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
...
@@ -1326,6 +1346,7 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
...
@@ -1326,6 +1346,7 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
height:
widget
.
height
,
height:
widget
.
height
,
scale:
_imageInfo
?.
scale
??
1.0
,
scale:
_imageInfo
?.
scale
??
1.0
,
color:
widget
.
color
,
color:
widget
.
color
,
opacity:
widget
.
opacity
,
colorBlendMode:
widget
.
colorBlendMode
,
colorBlendMode:
widget
.
colorBlendMode
,
fit:
widget
.
fit
,
fit:
widget
.
fit
,
alignment:
widget
.
alignment
,
alignment:
widget
.
alignment
,
...
...
packages/flutter/test/widgets/fade_in_image_test.dart
View file @
a84bb4eb
...
@@ -43,14 +43,12 @@ class FadeInImageParts {
...
@@ -43,14 +43,12 @@ class FadeInImageParts {
}
}
class
FadeInImageElements
{
class
FadeInImageElements
{
const
FadeInImageElements
(
this
.
rawImageElement
,
this
.
fadeTransitionElement
);
const
FadeInImageElements
(
this
.
rawImageElement
);
final
Element
rawImageElement
;
final
Element
rawImageElement
;
final
Element
?
fadeTransitionElement
;
RawImage
get
rawImage
=>
rawImageElement
.
widget
as
RawImage
;
RawImage
get
rawImage
=>
rawImageElement
.
widget
as
RawImage
;
FadeTransition
?
get
fadeTransition
=>
fadeTransitionElement
?.
widget
as
FadeTransition
?;
double
get
opacity
=>
rawImage
.
opacity
?.
value
??
1.0
;
double
get
opacity
=>
fadeTransition
==
null
?
1
:
fadeTransition
!.
opacity
.
value
;
}
}
class
LoadTestImageProvider
extends
ImageProvider
<
Object
>
{
class
LoadTestImageProvider
extends
ImageProvider
<
Object
>
{
...
@@ -78,11 +76,8 @@ FadeInImageParts findFadeInImage(WidgetTester tester) {
...
@@ -78,11 +76,8 @@ FadeInImageParts findFadeInImage(WidgetTester tester) {
final
Iterable
<
Element
>
rawImageElements
=
tester
.
elementList
(
find
.
byType
(
RawImage
));
final
Iterable
<
Element
>
rawImageElements
=
tester
.
elementList
(
find
.
byType
(
RawImage
));
ComponentElement
?
fadeInImageElement
;
ComponentElement
?
fadeInImageElement
;
for
(
final
Element
rawImageElement
in
rawImageElements
)
{
for
(
final
Element
rawImageElement
in
rawImageElements
)
{
Element
?
fadeTransitionElement
;
rawImageElement
.
visitAncestorElements
((
Element
ancestor
)
{
rawImageElement
.
visitAncestorElements
((
Element
ancestor
)
{
if
(
ancestor
.
widget
is
FadeTransition
)
{
if
(
ancestor
.
widget
is
FadeInImage
)
{
fadeTransitionElement
=
ancestor
;
}
else
if
(
ancestor
.
widget
is
FadeInImage
)
{
if
(
fadeInImageElement
==
null
)
{
if
(
fadeInImageElement
==
null
)
{
fadeInImageElement
=
ancestor
as
ComponentElement
;
fadeInImageElement
=
ancestor
as
ComponentElement
;
}
else
{
}
else
{
...
@@ -93,7 +88,7 @@ FadeInImageParts findFadeInImage(WidgetTester tester) {
...
@@ -93,7 +88,7 @@ FadeInImageParts findFadeInImage(WidgetTester tester) {
return
true
;
return
true
;
});
});
expect
(
fadeInImageElement
,
isNotNull
);
expect
(
fadeInImageElement
,
isNotNull
);
elements
.
add
(
FadeInImageElements
(
rawImageElement
,
fadeTransitionElement
));
elements
.
add
(
FadeInImageElements
(
rawImageElement
));
}
}
if
(
elements
.
length
==
2
)
{
if
(
elements
.
length
==
2
)
{
return
FadeInImageParts
(
fadeInImageElement
!,
elements
.
last
,
elements
.
first
);
return
FadeInImageParts
(
fadeInImageElement
!,
elements
.
last
,
elements
.
first
);
...
...
packages/flutter/test/widgets/image_test.dart
View file @
a84bb4eb
...
@@ -753,6 +753,19 @@ void main() {
...
@@ -753,6 +753,19 @@ void main() {
expect
(
renderer
.
colorBlendMode
,
BlendMode
.
clear
);
expect
(
renderer
.
colorBlendMode
,
BlendMode
.
clear
);
});
});
testWidgets
(
'Image opacity parameter'
,
(
WidgetTester
tester
)
async
{
const
Animation
<
double
>
opacity
=
AlwaysStoppedAnimation
<
double
>(
0.5
);
await
tester
.
pumpWidget
(
Image
(
excludeFromSemantics:
true
,
image:
_TestImageProvider
(),
opacity:
opacity
,
),
);
final
RenderImage
renderer
=
tester
.
renderObject
<
RenderImage
>(
find
.
byType
(
Image
));
expect
(
renderer
.
opacity
,
opacity
);
});
testWidgets
(
'Precache'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Precache'
,
(
WidgetTester
tester
)
async
{
final
_TestImageProvider
provider
=
_TestImageProvider
();
final
_TestImageProvider
provider
=
_TestImageProvider
();
late
Future
<
void
>
precache
;
late
Future
<
void
>
precache
;
...
@@ -1721,6 +1734,57 @@ void main() {
...
@@ -1721,6 +1734,57 @@ void main() {
skip:
kIsWeb
,
// https://github.com/flutter/flutter/issues/54292.
skip:
kIsWeb
,
// https://github.com/flutter/flutter/issues/54292.
);
);
testWidgets
(
'Image opacity'
,
(
WidgetTester
tester
)
async
{
final
Key
key
=
UniqueKey
();
await
tester
.
pumpWidget
(
RepaintBoundary
(
key:
key
,
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
Image
.
memory
(
Uint8List
.
fromList
(
kBlueRectPng
),
opacity:
const
AlwaysStoppedAnimation
<
double
>(
0.25
),
),
Image
.
memory
(
Uint8List
.
fromList
(
kBlueRectPng
),
opacity:
const
AlwaysStoppedAnimation
<
double
>(
0.5
),
),
Image
.
memory
(
Uint8List
.
fromList
(
kBlueRectPng
),
opacity:
const
AlwaysStoppedAnimation
<
double
>(
0.75
),
),
Image
.
memory
(
Uint8List
.
fromList
(
kBlueRectPng
),
opacity:
const
AlwaysStoppedAnimation
<
double
>(
1.0
),
),
],
),
));
// precacheImage is needed, or the image in the golden file will be empty.
if
(!
kIsWeb
)
{
final
Finder
allImages
=
find
.
byType
(
Image
);
for
(
final
Element
e
in
allImages
.
evaluate
())
{
await
tester
.
runAsync
(()
async
{
final
Image
image
=
e
.
widget
as
Image
;
await
precacheImage
(
image
.
image
,
e
);
});
}
await
tester
.
pumpAndSettle
();
}
await
expectLater
(
find
.
byKey
(
key
),
matchesGoldenFile
(
'transparent_image.png'
),
);
},
skip:
kIsWeb
,
// https://github.com/flutter/flutter/issues/54292.
);
testWidgets
(
'Reports image size when painted'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Reports image size when painted'
,
(
WidgetTester
tester
)
async
{
late
ImageSizeInfo
imageSizeInfo
;
late
ImageSizeInfo
imageSizeInfo
;
int
count
=
0
;
int
count
=
0
;
...
...
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