Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter draw SVG in CustomPaint (Canvas)

I have something like this:

CustomPaint(
       painter: CurvePainter(),
)

In this class I am doing my painting:

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import './myState.dart';
import './models/mode.dart';
final String rawSvg = '''<svg viewBox="...">...</svg>''';

class CurvePainter extends CustomPainter {
  MyState _myState;
  DrawableRoot svgRoot;
  CurvePainter(MyState myState) {
    this._myState = myState;
    this.loadAsset();
  }

  void loadAsset() async {
    this.svgRoot = await svg.fromSvgString(rawSvg, rawSvg);// The canvas that is your board.
  }

  @override
  void paint (Canvas canvas, Size size) {
    canvas.translate(_myState.translateX, _myState.translateY);
    if(this.svgRoot != null){
        svgRoot.scaleCanvasToViewBox(canvas, size);
        svgRoot.clipCanvasToViewBox(canvas);
        // svgRoot.draw(canvas, size);
    }
}

Somebody know how to draw a SVG inside paint method? I found this library https://pub.dev/packages/flutter_svg#-readme-tab- . With my code I get error: Unhandled Exception: Bad state: viewBox element must be 4 elements long

I would be nice, if I can scale and rotate the svg inside canvas. But this is optional.

like image 954
otto Avatar asked Sep 10 '19 15:09

otto


2 Answers

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.

like image 128
Dan Field Avatar answered Sep 17 '22 17:09

Dan Field


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();
      
    
    
like image 32
WarDoctor113 Avatar answered Sep 18 '22 17:09

WarDoctor113