Flutter draw SVG in CustomPaint (Canvas)

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

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 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
svgRoot.scaleCanvasToViewBox(canvas);

// Optional, but probably normally desireable: ensure the SVG isn't rendered
// outside of the viewbox bounds
svgRoot.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.