Flutter draw SVG in CustomPaint (Canvas) Flutter draw SVG in CustomPaint (Canvas) dart dart

Flutter draw SVG in CustomPaint (Canvas)


From the README:

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 usefinal 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 viewboxsvgRoot.scaleCanvasToViewBox(canvas);// Optional, but probably normally desireable: ensure the SVG isn't rendered// outside of the viewbox boundssvgRoot.clipCanvasToViewBox(canvas);svgRoot.draw(canvas, size);

Which you could adapt as:

class CurvePainter extends CustomPainter {  CurvePainter(this.svg);  final DrawableRoot svg;  @override  void paint(Canvas canvas, Size size) {       canvas.drawLine(...);       svg.scaleCanvasToViewBox(canvas);       svg.clipCanvasToViewBox(canvas);       svg.draw(canvas, size);  }}

I'd advise finding some way to get the asynchronous part earlier on in your app, perhaps using a FutureBuilder or a ValueListenableBuilder.

Disclosure: I'm the author/primary maintainer of Flutter SVG.


Ultimately I found drawing SVGs directly in Canvas to be cumbersome. Instead, I copied the SVG paths and transforms to Dart code using path_drawing and rendered them as Paths with Canvas.drawPath. This has the advantage of not even being an asset at all; the SVG data is literally code at this point. And you can convert back to an SVG easily. The process goes a bit like this:

  1. Add path_drawing: 0.4.1 to pubspec.yaml, flutter pub get, in the file you're rendering from import 'package:path_drawing/path_drawing.dart';.
  2. Copy-paste all paths from your SVG with the method parseSvgPathData as Path constants. (Path strings look something like M 86.102000,447.45700 L 86.102000,442.75300 .....)
    • You can combine many paths if there are more than one in the SVG:
      • static final Path complexPathToDraw = parseSvgPathData("path_1").addPath(parseSvgPathData("path_2"));.
  3. Usually the SVG will be wrapped in some translations (<g transform='translate()>). And drawPath can only render the Path from the top-left position. So you have to translate to the appropriate position. When rendering, translate the canvas before drawing (1) first to correct for the translations in the SVG, (2) next to scale to the size you want, (3) to go to the position you really want it on the Canvas. Then draw, and restore the Canvas to its untransformed state. But keep in mind, these matrices are added in reverse order to how we logically break it down because linear algebra is stupid.
    • canvas.save();canvas.translate(dxToRenderPosition, dyToRenderPosition);canvas.scale(sxFromSvgSizeToDesiredRenderSize);canvas.translate(dxFromSvg, dyFromSvg);canvas.drawPath(complexPathToDraw, Paint());canvas.restore();


I faced a problem similar to this where I wanted to draw a Svg scaled down on a small part of a canvas.

To make it work, I had to use this code:

Size desiredSize = Size(60, 40);// get the svg from a preloaded array of DrawableRoot corresponding to all the Svg I might usefinal DrawableRoot svgRoot = drawables[i];canvas.save();// [center] below is the Offset of the center of the area where I want the Svg to be drawncanvas.translate(center.dx - desiredSize.width / 2, center.dy - desiredSize.height / 2);Size svgSize = svgRoot.viewport.size;var matrix = Matrix4.identity();matrix.scale(desiredSize.width / svgSize.width, desiredSize.height / svgSize.height);canvas.transform(matrix.storage);svgRoot.draw(canvas, Rect.zero); // the second argument is not used in DrawableRoot.draw() methodcanvas.restore();

This way, you can have complex Svg rendered on the canvas and still do some work on it.

E.g.: you can draw multiple Svg on the same canvas and draw over them.