Add support for color and color blendmode in FadeInImage (#137681)
Added "color" and "colorBlendMode" props in [FadeInImage](https://api.flutter.dev/flutter/widgets/FadeInImage-class.html) and applied it to both placeholder and the target image. Added test to check weather both properties are applied to placeholder and the target image. Fixes https://github.com/flutter/flutter/issues/128229 Migration is not required because I have added two optional props and have not updated any existing code. Devs may choose to use them if they have a usecase. Existing projects should not get affected.(no errors or warning)
This commit is contained in:
parent
4b4a1feb46
commit
7faa6261c2
@ -84,6 +84,10 @@ class FadeInImage extends StatefulWidget {
|
|||||||
this.fadeOutCurve = Curves.easeOut,
|
this.fadeOutCurve = Curves.easeOut,
|
||||||
this.fadeInDuration = const Duration(milliseconds: 700),
|
this.fadeInDuration = const Duration(milliseconds: 700),
|
||||||
this.fadeInCurve = Curves.easeIn,
|
this.fadeInCurve = Curves.easeIn,
|
||||||
|
this.color,
|
||||||
|
this.colorBlendMode,
|
||||||
|
this.placeholderColor,
|
||||||
|
this.placeholderColorBlendMode,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.fit,
|
this.fit,
|
||||||
@ -140,6 +144,10 @@ class FadeInImage extends StatefulWidget {
|
|||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.fit,
|
this.fit,
|
||||||
|
this.color,
|
||||||
|
this.colorBlendMode,
|
||||||
|
this.placeholderColor,
|
||||||
|
this.placeholderColorBlendMode,
|
||||||
this.placeholderFit,
|
this.placeholderFit,
|
||||||
this.filterQuality = FilterQuality.low,
|
this.filterQuality = FilterQuality.low,
|
||||||
this.placeholderFilterQuality,
|
this.placeholderFilterQuality,
|
||||||
@ -198,6 +206,10 @@ class FadeInImage extends StatefulWidget {
|
|||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.fit,
|
this.fit,
|
||||||
|
this.color,
|
||||||
|
this.colorBlendMode,
|
||||||
|
this.placeholderColor,
|
||||||
|
this.placeholderColorBlendMode,
|
||||||
this.placeholderFit,
|
this.placeholderFit,
|
||||||
this.filterQuality = FilterQuality.low,
|
this.filterQuality = FilterQuality.low,
|
||||||
this.placeholderFilterQuality,
|
this.placeholderFilterQuality,
|
||||||
@ -254,6 +266,46 @@ class FadeInImage extends StatefulWidget {
|
|||||||
/// also affected by the scale factor.
|
/// also affected by the scale factor.
|
||||||
final double? width;
|
final double? width;
|
||||||
|
|
||||||
|
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
|
||||||
|
///
|
||||||
|
/// Color applies to the [image].
|
||||||
|
///
|
||||||
|
/// See Also:
|
||||||
|
///
|
||||||
|
/// * [placeholderColor], the color which applies to the [placeholder].
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
/// Used to combine [color] with this [image].
|
||||||
|
///
|
||||||
|
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
|
||||||
|
/// the source and this image is the destination.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
|
||||||
|
/// * [placeholderColorBlendMode], the color blend mode which applies to the [placeholder].
|
||||||
|
final BlendMode? colorBlendMode;
|
||||||
|
|
||||||
|
/// If non-null, this color is blended with each placeholder image pixel using [placeholderColorBlendMode].
|
||||||
|
///
|
||||||
|
/// Color applies to the [placeholder].
|
||||||
|
///
|
||||||
|
/// See Also:
|
||||||
|
///
|
||||||
|
/// * [color], the color which applies to the [image].
|
||||||
|
final Color? placeholderColor;
|
||||||
|
|
||||||
|
/// Used to combine [placeholderColor] with the [placeholder] image.
|
||||||
|
///
|
||||||
|
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [placeholderColor] is
|
||||||
|
/// the source and this placeholder is the destination.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
|
||||||
|
/// * [colorBlendMode], the color blend mode which applies to the [image].
|
||||||
|
final BlendMode? placeholderColorBlendMode;
|
||||||
|
|
||||||
/// If non-null, require the image to have this height.
|
/// If non-null, require the image to have this height.
|
||||||
///
|
///
|
||||||
/// If null, the image will pick a size that best preserves its intrinsic
|
/// If null, the image will pick a size that best preserves its intrinsic
|
||||||
@ -361,6 +413,8 @@ class _FadeInImageState extends State<FadeInImage> {
|
|||||||
ImageErrorWidgetBuilder? errorBuilder,
|
ImageErrorWidgetBuilder? errorBuilder,
|
||||||
ImageFrameBuilder? frameBuilder,
|
ImageFrameBuilder? frameBuilder,
|
||||||
BoxFit? fit,
|
BoxFit? fit,
|
||||||
|
Color? color,
|
||||||
|
BlendMode? colorBlendMode,
|
||||||
required FilterQuality filterQuality,
|
required FilterQuality filterQuality,
|
||||||
required Animation<double> opacity,
|
required Animation<double> opacity,
|
||||||
}) {
|
}) {
|
||||||
@ -372,6 +426,8 @@ class _FadeInImageState extends State<FadeInImage> {
|
|||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
fit: fit,
|
fit: fit,
|
||||||
|
color: color,
|
||||||
|
colorBlendMode: colorBlendMode,
|
||||||
filterQuality: filterQuality,
|
filterQuality: filterQuality,
|
||||||
alignment: widget.alignment,
|
alignment: widget.alignment,
|
||||||
repeat: widget.repeat,
|
repeat: widget.repeat,
|
||||||
@ -388,6 +444,8 @@ class _FadeInImageState extends State<FadeInImage> {
|
|||||||
errorBuilder: widget.imageErrorBuilder,
|
errorBuilder: widget.imageErrorBuilder,
|
||||||
opacity: _imageAnimation,
|
opacity: _imageAnimation,
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
|
color: widget.color,
|
||||||
|
colorBlendMode: widget.colorBlendMode,
|
||||||
filterQuality: widget.filterQuality,
|
filterQuality: widget.filterQuality,
|
||||||
frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
|
frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
|
||||||
if (wasSynchronouslyLoaded || frame != null) {
|
if (wasSynchronouslyLoaded || frame != null) {
|
||||||
@ -400,6 +458,8 @@ class _FadeInImageState extends State<FadeInImage> {
|
|||||||
image: widget.placeholder,
|
image: widget.placeholder,
|
||||||
errorBuilder: widget.placeholderErrorBuilder,
|
errorBuilder: widget.placeholderErrorBuilder,
|
||||||
opacity: _placeholderAnimation,
|
opacity: _placeholderAnimation,
|
||||||
|
color: widget.placeholderColor,
|
||||||
|
colorBlendMode: widget.placeholderColorBlendMode,
|
||||||
fit: widget.placeholderFit ?? widget.fit,
|
fit: widget.placeholderFit ?? widget.fit,
|
||||||
filterQuality: widget.placeholderFilterQuality ?? widget.filterQuality,
|
filterQuality: widget.placeholderFilterQuality ?? widget.filterQuality,
|
||||||
),
|
),
|
||||||
|
@ -42,6 +42,8 @@ class FadeInImageElements {
|
|||||||
double get opacity => rawImage.opacity?.value ?? 1.0;
|
double get opacity => rawImage.opacity?.value ?? 1.0;
|
||||||
BoxFit? get fit => rawImage.fit;
|
BoxFit? get fit => rawImage.fit;
|
||||||
FilterQuality? get filterQuality => rawImage.filterQuality;
|
FilterQuality? get filterQuality => rawImage.filterQuality;
|
||||||
|
Color? get color => rawImage.color;
|
||||||
|
BlendMode? get colorBlendMode => rawImage.colorBlendMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadTestImageProvider extends ImageProvider<Object> {
|
class LoadTestImageProvider extends ImageProvider<Object> {
|
||||||
@ -378,6 +380,29 @@ void main() {
|
|||||||
expect(findFadeInImage(tester).target.opacity, moreOrLessEquals(1));
|
expect(findFadeInImage(tester).target.opacity, moreOrLessEquals(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgetsWithLeakTracking('Image color and colorBlend parameters', (WidgetTester tester) async {
|
||||||
|
final TestImageProvider placeholderProvider = TestImageProvider(placeholderImage);
|
||||||
|
final TestImageProvider imageProvider = TestImageProvider(targetImage);
|
||||||
|
|
||||||
|
await tester.pumpWidget(FadeInImage(
|
||||||
|
placeholder: placeholderProvider,
|
||||||
|
image: imageProvider,
|
||||||
|
color: const Color(0xFF00FF00),
|
||||||
|
colorBlendMode: BlendMode.clear,
|
||||||
|
placeholderColor: const Color(0xFF0000FF),
|
||||||
|
placeholderColorBlendMode: BlendMode.modulate,
|
||||||
|
fadeOutDuration: animationDuration,
|
||||||
|
fadeInDuration: animationDuration,
|
||||||
|
excludeFromSemantics: true,
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(findFadeInImage(tester).placeholder?.color, const Color(0xFF0000FF));
|
||||||
|
expect(findFadeInImage(tester).placeholder?.colorBlendMode, BlendMode.modulate);
|
||||||
|
await tester.pump(animationDuration);
|
||||||
|
expect(findFadeInImage(tester).target.color, const Color(0xFF00FF00));
|
||||||
|
expect(findFadeInImage(tester).target.colorBlendMode, BlendMode.clear);
|
||||||
|
});
|
||||||
|
|
||||||
group('ImageProvider', () {
|
group('ImageProvider', () {
|
||||||
|
|
||||||
test('memory placeholder cacheWidth and cacheHeight is passed through', () async {
|
test('memory placeholder cacheWidth and cacheHeight is passed through', () async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user