用flutter_svg在Flutter中实现SVG

8,732 阅读5分钟

可扩展矢量图(SVG)是应用程序中最广泛使用的文件图像格式之一。因为它比位图文件有几个优点,特别是在缩放时保留图像质量方面,如果不首先考虑使用SVG,就很难开始构建一个Flutter应用程序

在这篇文章中,您将学习如何以及何时在Flutter应用程序中使用SVG文件。

在 Flutter 中使用 SVG

Skia,一个2D图形库和Flutter的核心组件,只能将图像序列化为SVG文件。因此,用Skia解码或渲染SVG图像是不可能的。幸运的是,社区用一个梦幻般的Dart原生包来拯救我们,这个包叫做flutter_svg,可以快速渲染和解码SVG。

什么是flutter_svg?

flutter_svg包实现了一个图片缓存,存储了一个ui:Picture 类。这是Skia图形引擎的SkPicture 包装器,它以二进制模式记录特定的SVG渲染命令。

ui.Picture 类并不消耗太多内存,并且被缓存起来以避免重复解析XML文件。让我们来看看SvgPicture.asset 的构造函数。

SvgPicture.asset(
  String assetName, {
  Key? key,
  this.matchTextDirection = false,
  AssetBundle? bundle,
  String? package,
  this.width,
  this.height,
  this.fit = BoxFit.contain,
  this.alignment = Alignment.center,
  this.allowDrawingOutsideViewBox = false,
  this.placeholderBuilder,
  Color? color,
  BlendMode colorBlendMode = BlendMode.srcIn,
  this.semanticsLabel,
  this.excludeFromSemantics = false,
  this.clipBehavior = Clip.hardEdge,
  this.cacheColorFilter = false,
})  : pictureProvider = ExactAssetPicture(
        allowDrawingOutsideViewBox == true
            ? svgStringDecoderOutsideViewBox
            : svgStringDecoder,
        assetName,
        bundle: bundle,
        package: package,
        colorFilter: svg.cacheColorFilterOverride ?? cacheColorFilter
            ? _getColorFilter(color, colorBlendMode)
            : null,
      ),
      colorFilter = _getColorFilter(color, colorBlendMode),
      super(key: key);

通过观察实现,你会注意到来自pictureProvider 的流通知会更新SvgPicture 的图片。

 void _resolveImage() {
final PictureStream newStream = widget.pictureProvider
.resolve(createLocalPictureConfiguration(context));
assert(newStream != null); // ignore: unnecessary_null_comparison
_updateSourceStream(newStream);
}

在这个代码块中,来自pictureProvider 的流被图片缓存的完成者填充到ui.Picture

PictureStream resolve(PictureConfiguration picture,
    {PictureErrorListener? onError}) {
  // ignore: unnecessary_null_comparison
  assert(picture != null);
  final PictureStream stream = PictureStream();
  T? obtainedKey;
  obtainKey(picture).then<void>(
    (T key) {
      obtainedKey = key;
      stream.setCompleter(
        cache.putIfAbsent(
          key!,
          () => load(key, onError: onError),
        ),
      );
    },
  ).catchError((Object exception, StackTrace stack) async {
    if (onError != null) {
      onError(exception, stack);
      return;
    }
    FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'SVG',
        context: ErrorDescription('while resolving a picture'),
        silent: true, // could be a network error or whatnot
        informationCollector: () sync* {
          yield DiagnosticsProperty<PictureProvider>(
              'Picture provider', this);
          yield DiagnosticsProperty<T>('Picture key', obtainedKey,
              defaultValue: null);
        }));
  });
  return stream;
}

添加flutter_svg插件

要将此包添加到您的Flutter依赖项中,您可以运行。

flutter pub add flutter_svg

或者,您可以将flutter_svg添加到您的pubspec.yaml 文件中。

dependencies:
  flutter_svg: ^0.22.0

确保您在您的终端或使用您的编辑器运行flutter pub get 。安装完成后,在您的Dart代码中您想使用这个包的地方导入这个包。

import 'package:flutter_svg/flutter_svg.dart';

在您的Flutter应用程序中使用flutter_svg

有几种方法可以使用这个包,但我们将介绍最常见的使用情况。

一种选择是从内部SVG文件中加载SVG,该文件通常存储在asset 文件夹中。

// example
final String assetName = 'assets/image.svg';
final Widget svg = SvgPicture.asset(
assetName,
);

你也可以从一个URL中加载一个SVG文件,像这样。

// example
final Widget networkSvg = SvgPicture.network(
  'https://site-that-takes-a-while.com/image.svg',
);

最后,你可以从一个SVG代码中加载一个SVG。

// example
SvgPicture.string(
  '''<svg viewBox="...">...</svg>'''
);

在Flutter中扩展SVG功能

一旦您加载了您的SVG文件,第一步就是改变图像的颜色或色调。

// example
final String assetName = 'assets/up_arrow.svg';
final Widget svgIcon = SvgPicture.asset(
  assetName,
  color: Colors.red,
);

使用语义标签有助于描述图像的目的,增强可访问性。为了实现这一点,你可以添加semanticsLabel 参数。语义标签将不会显示在用户界面中。

// example
final String assetName = 'assets/up_arrow.svg';
final Widget svgIcon = SvgPicture.asset(
  assetName,
  color: Colors.red,
  semanticsLabel: 'A red up arrow'
);

SvgPicture如果在heightwidth 上没有指定,flutter_svg包会渲染一个空框,LimitedBox ,作为默认的占位符。然而,如果在heightwidth 上指定了SvgPicture ,将呈现一个SizedBox ,以确保更好的布局体验。

不过,占位符可以被替换,这对改善用户体验非常好,特别是在通过网络请求加载资产时,可能会有延迟。

// example
final Widget networkSvg = SvgPicture.network(
  'https://site-that-takes-a-while.com/image.svg',
  semanticsLabel: 'A shark?!',
  placeholderBuilder: (BuildContext context) => Container(
      padding: const EdgeInsets.all(30.0),
      child: const CircularProgressIndicator()),
);

在这个例子中,我选择了CircularProgressIndicator ,在图片加载时显示一个进度指示器。你可以添加任何你想要的其他小部件。例如,您可以使用一个自定义的加载部件来替代CircularProgressIndicator

检查SVG与flutter_svg的兼容性

您应该知道,flutter_svg库并不支持所有的SVG功能。然而,该包确实提供了一个辅助方法,以确保你不会因为缺乏支持的功能而渲染出一个破损的图像。

// example 
final SvgParser parser = SvgParser();
try {
  parser.parse(svgString, warningsAsErrors: true);
  print('SVG is supported');
} catch (e) {
  print('SVG contains unsupported features');
}

请注意,该库目前只检测不支持的元素,如<style> 标签,但不识别不支持的属性。

推荐的Adobe Illustrator SVG配置

为了在Adobe Illustrator中最大限度地利用flutter_svg,您需要遵循特定建议。

      • 样式:选择展示属性,而不是内联CSS,因为CSS不完全被支持
      • 图像:选择嵌入,而不是链接到另一个文件,以获得一个不依赖其他文件的单一SVG
      • 对象ID:选择图层名称,以便为SVG标签的每个图层添加一个名称,或者使用最小的图层名称--选择权在你手中

SVG flutter image options

在另一个画布中渲染SVG文件

有的时候,你可能想把你的SVG渲染到另一个画布上。SVGPicture ,帮助你轻松实现这一目标。

// example
final String rawSvg = '''<svg viewBox="...">...</svg>''';
final DrawableRoot svgRoot = await svg.fromSvgString(rawSvg, rawSvg);
final Picture picture = svgRoot.toPicture();
svgRoot.scaleCanvasToViewBox(canvas);
svgRoot.clipCanvasToViewBox(canvas);
svgRoot.draw(canvas, size);

结论

使用SVG文件可以成为您的Flutter应用程序的一个重要补充,但SVG并不总是您所有图像问题的正确答案。观察你的用例并持续测量你的应用程序和SVG的性能是至关重要的,因为你可能需要用另一种标准的图像格式(如PNG或JPEG)来替换SVG。

尽管Flutter并不支持SVG,但Dart原生的flutter_svg包对SVG文件有很好的性能和快速支持。该包的使用也相对简单。

请记住,该包的版本仍然低于1.0.0,这可能会破坏API的变化。然而,作者在保持API尽可能的稳定方面做得很好。当使用flutter_svg时,请确保你总是在 pub.dev上检查软件包的最新版本以保持最新。谢谢你的阅读!

The postImplementing SVG in Flutter with flutter_svgappeared first onLogRocket Blog.