[译]Flutter Favorite之SVG渲染库flutter_svg

1,331 阅读5分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

本文翻译自pub: flutter_svg | Flutter Package (flutter-io.cn)

译时版本: flutter_svg 1.0.3

看下 pub 示例的运行结果,有木有很惊艳!

iShot2022-01-22 11.13.57.png


flutter_svg

在 Flutter 组件上绘制 SVG(和一些 Android VectorDrawable (XML))。

开始

这是一个 Dart 原生渲染库。Issue/PR 会被提到 Flutter ,flutter/engine 并非用于 Dart 实现的良好的候选特性所需(特别是如果它们无法离开引擎支持来实现)。 尽管如此,不是所有能用 Skia 轻易实现的事情都需要用 Skia 来做。例如,这里的路径解析逻辑用原来来做也不会慢很多,并且 Skia 也不是如所想的总是用来进行低级别的 GPU 加速工作(例如,Dash 路径)。

现在所有 assets/ 文件下的 SVG(除了文本相关的一些)都在 golden/ 文件夹下有对应的 PNG,它们使用 flutter test tool/gen_golden.dart 来渲染,并且在 Chrome 里对它们的渲染输出进行比较。自动测试会继续比较它们来确保代码的改变不会破坏已知良好的渲染。

基本用法(从 asset 创建一个 SVG 渲染组件):

final String assetName = 'assets/image.svg';
final Widget svg = SvgPicture.asset(
  assetName,
  semanticsLabel: 'Acme Logo'
);

可以如下给图片着色:

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

默认的占位组件是一个空的 Box(LimitedBox) - 虽然如果为 SvgPicture 指定高度和宽度的话,会用 SizedBox 代替(这能保证更好的布局体验)。现在还没有展现错误可视化的方式,即使在调式模式下错误会被适当地输出到控制台。

也可以指定一个占位组件。占位组件会在解析/加载过程中显示(通常会和访问网络相关)。

// Will print error messages to the console.
// 会在控制台打印错误信息
final String assetName = 'assets/image_that_does_not_exist.svg';
final Widget svg = SvgPicture.asset(
  assetName,
);

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()),
);

如果想在一些其它的 Canvas 上渲染 SVG,可以如下来做:

import 'package:flutter_svg/flutter_svg.dart';
final String rawSvg = '''<svg viewBox="...">...</svg>''';
final DrawableRoot svgRoot = await svg.fromSvgString(rawSvg, rawSvg);

// If you only want the final Picture output, just use
// 如果只想最终的图片输出,只需
final Picture picture = svgRoot.toPicture();

// Otherwise, if you want to draw it to a canvas:
// Optional, but probably normally desirable: scale the canvas dimensions to
// the SVG's viewbox
// 否则,如果想在一个 canvas 上绘制:
// 作为可选项,但是可能通常也需要:把 canvs 缩放到 SVG 的 viewbox。
svgRoot.scaleCanvasToViewBox(canvas);

// Optional, but probably normally desireable: ensure the SVG isn't rendered
// outside of the viewbox bounds
// 作为可选项,但是可能通常也需要:确保 SVG 没有渲染到 viewbox 的边界之外
svgRoot.clipCanvasToViewBox(canvas);
// The second parameter is not used
// 第二个参数未被使用
svgRoot.draw(canvas, null);

SvgPicture 会帮助自动化这个逻辑,它会提供一些便利的包装,用于从多个源获取 asset 并缓存相关的 Picture 。 任何时点,它都不会渲染数据到 Image ,你可以在 Flutter 中来做这个,但是一开始就会失去使用矢量格式的好处。

我们正在尽所有努力来避免不必要的 API 改变,现在还不能确保稳定(因此是 1.0.0 之前的版本)。迄今为止,最大的改动是不再建议使用用于 SvgPictureSvgImage - 它会使维护名称变得比较难以理解,因为 Picture 是渲染的底层机制而不是 Image

查看 [main.dart] 的完事示例。

检查 SVG 兼容性

该库并没有支持所有的 SVG 特性(参考下面),有时我们需要动态检查一个 SVG 是否包含不支持的特性造成破损的图像。在测试中你可能也想抛出错误,但只在运行时给出相关警告。可以使用下面的代码片段来实现:

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

注意: 该库现在只检测不支持的元素 (如 <style> 标签),但是不检测不支持的属性。

使用场景

  • 设计者创建了一个矢量资源,你想将其包含到工程中,但不需要转换为5个不同的光栅格式的解决方案。
  • 你的矢量绘图不用作静态资源,也没有(或最小)交互。
  • 你想在运行时从网络动态加载 SVG 。
  • 你想绘制 SVG ,然后将其渲染到 image 。

范围之外 / 非目标

  • SMIL 动画。这看上去就很疯狂。我认为它会使 SVG 动画化成为可能,但是用更多 Fluter 驱动的方式。
  • SVG 的交互/事件
  • 任何 CSS 支持 - 用 usvg 或 scour 预处理你的 SVG 来处理所有的 CSS ?
  • SVG 脚本
  • 外部元素
  • 渲染属性/提示

Adobe Illustrator SVG 的建议配置

  • Styling:选择 Presentation Attributes 而不是 Inline CSS ,因为 CSS 没有被完全支持。
  • Images: 选择 Embded 不链接到其它文件来得到无其它文件依赖的单个 SVG 。
  • Objects IDs: 选择 layer names 将每层的名称添加到 SVG 标签,或者你可以使用 minimal ,它是可选的。

image.png

SVG 示例所属 

/assets/w3samples 中的 SVG 是从 W3 sample files 拉取的。

/assets/deborah_ufw 中的 SVG 由 @deborah-ufw 提供。

/assets/simple 的 SVG 是从一些细小的示例中拉取或者生成用于测试基本功能 - 其中的一些直接来源于 SVG 1.1 规范. 一些也是来源于或改编自仓库中的 issue 。

/assets/wikimedia 的 SVG 是从 Wikimedia Commons 拉取的。

/assets/android_vd 的 Android Drawable 是从 Android 文档和示例提取的。

Flutter Logo 基于Flutter Logo 组件 © Google 创建。

Dart logo 来自于 dartlang.org © Google 。

/assets/noto-emoji 的 SVG 来自于 Google i18n noto-emoji,使用 Apache 许可证。

烦请提出无法正确渲染的 SVG(例如,在 Chrome 中能正常渲染,在这里不能正常渲染的),只要它们没有使用任何“可能范围外”的内容(上面所述)。

替代方案