Skip to content
DevPenh

SVG to Flutter: CustomPainter vs flutter_svg

Choose between generated CustomPainter code and the flutter_svg package, with tested examples for icons, colors and animated paths.

By Yorn Chanpenh · Last reviewed

Flutter can draw an SVG through the flutter_svg package or through a CustomPainter generated from the path data. They look similar on screen, but they have different costs: one adds a dependency and keeps the SVG file; the other adds generated Dart code and gives you the Path directly.

The Dart examples were checked with Flutter 3.47.9 / Dart 3.13.7 and flutter_svg 2.1.0. The SvgPicture.string widget test and the CustomPainter paint test pass flutter test.

Choose by what you need to change

NeedUse
Display many SVG files as they areflutter_svg
No extra package, or a small fixed icon setGenerated CustomPainter
Animate or change individual pathsGenerated CustomPainter
Clip or hit-test with the shapeGenerated CustomPainter
Text, gradients, filters or complex SVG featuresflutter_svg, after checking the file

Option 1: flutter_svg

Add the package, put the file in assets/, and declare it in pubspec.yaml:

flutter pub add flutter_svg

# pubspec.yaml
flutter:
  assets:
    - assets/icons/

Then render it like any other widget:

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

class CheckIcon extends StatelessWidget {
  const CheckIcon({super.key});

  @override
  Widget build(BuildContext context) {
    return SvgPicture.asset(
      'assets/icons/check.svg',
      width: 24,
      height: 24,
      colorFilter: const ColorFilter.mode(Colors.black, BlendMode.srcIn),
    );
  }
}

colorFilter replaces the rendered colour, which is useful for theme colours. It applies to the whole picture, so it cannot tint separate parts differently. The same widget works with SvgPicture.string(svgSource) for a string.

Option 2: generated CustomPainter

Paste the SVG into the SVG to Flutter converter. It produces a Path and a painter. A small painter looks like this:

import 'package:flutter/material.dart';

class IconPainter extends CustomPainter {
  final Path path;
  final Color color;

  const IconPainter(this.path, {this.color = const Color(0xFF1E88E5)});

  @override
  void paint(Canvas canvas, Size size) {
    final scale = size.width / 24;
    canvas.scale(scale, scale);
    canvas.drawPath(path, Paint()..color = color);
  }

  @override
  bool shouldRepaint(covariant IconPainter oldDelegate) =>
      oldDelegate.path != path || oldDelegate.color != color;
}

Use it inside a sized box so the 24-unit viewBox scales correctly:

SizedBox(
  width: 24,
  height: 24,
  child: CustomPaint(
    painter: IconPainter(path),
  ),
)

The converter handles paths, basic shapes, transforms and inherited styles. It skips text, gradients, patterns, <use>, images, masks, filters, markers and dashed strokes. The page lists every skipped feature, so check that list before replacing an existing SVG.

Colours are not the same format

Flutter uses Color(0xAARRGGBB), with alpha first. An SVG colour such as #1E88E5 becomes Color(0xFF1E88E5) when it is opaque. If the converter output has the wrong colour or alpha, use the Flutter Color converter to check the value.

When the SVG is not a good fit

  • Text: convert text to paths in the editor first. Neither approach treats a font as a portable shape.
  • Gradients: the painter falls back to a solid colour; a Shader must be added by hand.
  • Many changing icons: flutter_svg is usually simpler than regenerating painters.
  • Animation: a painter gives you the Path and a repaint notifier, but you still write the animation controller and interpolation.

Test the rendered result

A widget test can confirm the widget builds and paints without throwing:

testWidgets('renders the SVG icon', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: Scaffold(
        body: SvgPicture.string(
          svgSource,
          width: 24,
          height: 24,
        ),
      ),
    ),
  );

  expect(find.byType(SvgPicture), findsOneWidget);
});

For a generated painter, draw it into a PictureRecorder and assert that painting completes; this catches malformed path data without needing a golden image:

test('paints the generated path', () {
  final recorder = PictureRecorder();
  final canvas = Canvas(recorder);
  IconPainter(path).paint(canvas, const Size(24, 24));
  recorder.endRecording().dispose();
});

Start with the SVG converter

The SVG to Flutter converter shows the generated path, the painter and the skipped-feature list. If the file uses unsupported features, keep it as an asset with flutter_svg instead of rewriting the missing parts by hand. For the full comparison of Flutter drawing options, see the Flutter Color from hex guide and the Flutter JSON serialization guide.

Part of the API debugging series. Start with How to Debug API Responses: A Practical Workflow.