首页 > 解决方案 > Flutter ShaderMask 使用 ImageShader

问题描述

我正在尝试ShaderMask使用图像作为着色器。我正在从内存中读取图像,所以我的图像是File. 如何使用内存中的图像创建 ImageShader?

File imageFile;
Image image = Image.file(imageFile)

ShaderMask(
   shaderCallback: (bounds) {
      Float64List matrix4 = new Matrix4.identity().storage; // <--- DO I NEED THIS OR THE BOUNDS?
      return ImageShader(image, TileMode.mirror, TileMode.mirror, matrix4);
   },
   child: child
)

由于图像类型错误ImageShader(我需要 ui.Image,我不明白如何创建),因此出现错误。

如何ImageShaderFile图像创建?

PS:是matrix4正确的还是我应该以某种方式使用边界?

标签: flutter

解决方案


重要的是要知道它ImageShader使用dart:ui包中的图像而不是Image小部件的实例。没有可用于ui.Image从网络位置、文件或资产创建实例的直接操作或构造函数,因此您需要一些代码来完成它。

在查找许多资源并深入研究小部件如何加载原始图像的代码后,我想出的最佳通用解决方案Image是使用ImageProvider作为源。抽象ImageProvider具有像NetworkImage,和之类的实现FileImage,可以从您想要的任何资源加载您的图像。ExactAssetImageMemoryImage

首先你得到一个ImageStreamusingImageProvider.resolve方法。该resolve方法采用 type 的参数,该参数ImageConfiguration应填充您在代码位置可用的尽可能多的信息。在大多数情况下,您可以使用全局函数,但请注意,当您在StatefulWidgetcreateLocalImageConfiguration的方法中创建着色器时,这将不起作用。initState

在已解决的情况下,ImageStream您可以附加一个ImageStreamListener, 将ImageListener回调作为第一个参数。加载图像后,将使用 调用回调ImageInfo,它在属性上提供请求的图像image

您可以ImageShader使用这两种平铺模式TileMode.clamp和一个简单的单位矩阵来构建 ,您可以手动创建或使用Matrix4该类提供的那个。如果您需要图像着色器小于所提供图像的大小,您可以将您的提供程序包装在 an 中ResizeProvider并指定所需的宽度和高度。

下面我实现了一个ImageMask小部件作为参考,它可以用来屏蔽任何类型的小部件。

class ImageMask extends StatefulWidget {
  final ImageProvider image;
  final double width;
  final double height;
  final Widget child;

  const ImageMask({@required this.image, this.width, this.height, @required this.child});

  @override
  _ImageMaskState createState() => _ImageMaskState();
}

class _ImageMaskState extends State<ImageMask> {
  Future<Shader> _shader;

  @override
  void initState() {
    super.initState();
    _shader = _loadShader(context);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _shader,
      builder: (_, AsyncSnapshot<Shader> snapshot) {
        return snapshot.connectionState != ConnectionState.done || snapshot.hasError
            ? SizedBox(width: widget.width, height: widget.height)
            : ShaderMask(
                blendMode: BlendMode.dstATop,
                shaderCallback: (bounds) => snapshot.data,
                child: widget.child,
              );
      },
    );
  }

  Future<Shader> _loadShader(BuildContext context) async {
    final completer = Completer<ImageInfo>();

    // use the ResizeImage provider to resolve the image in the required size
    ResizeImage(widget.image, width: widget.width.toInt(), height: widget.height.toInt())
        .resolve(ImageConfiguration(size: Size(widget.width, widget.height)))
        .addListener(ImageStreamListener((info, _) => completer.complete(info)));

    final info = await completer.future;
    return ImageShader(
      info.image,
      TileMode.clamp,
      TileMode.clamp,
      Float64List.fromList(Matrix4.identity().storage),
    );
  }
}

推荐阅读