使用ImageShader的Flutter ShaderMask

时间:2020-06-22 18:59:04

标签: flutter

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

ShaderMask

File出现错误,因为图像类型错误(我需要ui.Image,但我不知道如何创建)。

如何从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

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

1 个答案:

答案 0 :(得分:2)

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

在查找许多资源并深入研究代码后,Image小部件如何加载原始图像时,我想到的最好的通用解决方案是使用ImageProvider作为源。抽象ImageProvider具有NetworkImageFileImageExactAssetImageMemoryImage之类的实现,可以从所需的任何资源中加载图像。

首先,您使用ImageStream方法获得一个ImageProvider.resolveresolve方法采用ImageConfiguration类型的参数,该参数应填充代码位置可用的尽可能多的信息。在大多数情况下,您可以使用全局createLocalImageConfiguration函数,但是请注意,当您在StatefulWidget的initState方法中创建着色器时,此功能将无效。

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

您可以将ImageShader的两个切片模式都构造为TileMode.clamp和一个简单的单位矩阵,可以手动创建或采用Matrix4类提供的那个矩阵。如果您需要图像着色器小于提供的图像大小,则可以将提供程序包装在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),
    );
  }
}