SVG in Flutter: the flutter_svg package explained

Flutter's Image widget does not read SVG. How to show one with flutter_svg and SvgPicture: assets, network files, size, colour, icons and its limits.

Published

Flutter’s built-in Image widget does not list SVG among its formats: its documentation names JPEG, PNG, GIF, WebP, BMP and WBMP, and says anything further depends on the platform. To show an SVG, add the flutter_svg package, published by the Flutter team on pub.dev, and use its SvgPicture widget:

import 'package:flutter_svg/flutter_svg.dart';

const assetName = 'assets/dart.svg';
final Widget svg = SvgPicture.asset(assetName, semanticsLabel: 'Dart Logo');

This page follows the README and source of flutter_svg 2.3.0, the current release on pub.dev at the time of writing. Its Dart examples are taken from that documentation, or written to match the constructors in the source and Flutter’s API documentation, and were not run on a device. Version 2.3.0 asks for Flutter 3.35 or later.

Set it up

Add the package. In the project folder, run:

flutter pub add flutter_svg

This writes flutter_svg: ^2.3.0 under dependencies in pubspec.yaml.

List the file as an asset. Put the SVG in the project, for example in an assets folder, and name it in pubspec.yaml:

flutter:
  assets:
    - assets/dart.svg

To include every file in a folder, list the folder with a slash at the end: - assets/. Flutter’s documentation notes that only files directly in that folder are included, so each subfolder needs its own line, and that assets: must be indented by exactly two spaces under flutter:.

Import and use it. Import the package in the Dart file, as in the example above, and call SvgPicture.asset with the same path that is written in pubspec.yaml.

Where the SVG comes from

SvgPicture has a constructor for each source:

ConstructorTakesUse for
SvgPicture.asset(name)The asset’s pathFiles shipped with the app
SvgPicture.network(url)A URLFiles fetched from a server
SvgPicture.string(text)SVG markup as a StringSVG built in code or received from an API
SvgPicture.file(file)A FileFiles on the device
SvgPicture.memory(bytes)A Uint8ListSVG data already in memory

From a string:

const svgString = '''
<svg viewBox="0 0 100 100">
  <rect width="50" height="50" fill="#FF0000" />
  <circle cx="75" cy="75" r="25" fill="#00FF00" />
</svg>
''';
final Widget picture = SvgPicture.string(svgString);

From the network, with something to show while it loads:

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

The package’s source notes that network pictures are cached whatever the HTTP headers say, and that a headers argument sends custom headers with the request.

Size and fit

ArgumentMeaningDefault
width, heightThe size of the widgetThe size of its parent
fitHow the picture is fitted into that spaceBoxFit.contain
alignmentWhere the picture sits when there is room to spareAlignment.center
SvgPicture.asset('assets/dart.svg', width: 48, height: 48)

The package’s documentation advises giving both width and height, or placing the widget where its parent fixes the size. Otherwise the size changes when the file finishes loading and the layout jumps. BoxFit.contain scales the whole picture to fit inside the space without distorting it, as an SVG does in a browser by default.

The picture is clipped to its viewBox. allowDrawingOutsideViewBox: true turns that off, and the documentation says to use it with caution.

Colour

To paint the whole picture in one colour, as for an icon, pass a colour filter:

final Widget svgIcon = SvgPicture.asset(
  'assets/simple/dash_path.svg',
  colorFilter: const ColorFilter.mode(Colors.red, BlendMode.srcIn),
  semanticsLabel: 'Red dash paths',
);

BlendMode.srcIn keeps the shape of the drawing and replaces every colour in it with red. It suits single-colour icons and flattens anything with several colours. The older color and colorBlendMode arguments are marked as deprecated in the source in favour of colorFilter.

To change some colours and keep others, version 2.1.0 added colorMapper. Write a class that extends ColorMapper, and its substitute method is called for every colour found while the SVG is read:

class _MyColorMapper extends ColorMapper {
  const _MyColorMapper();

  @override
  Color substitute(String? id, String elementName, String attributeName, Color color) {
    if (color == const Color(0xFFFF0000)) {
      return Colors.blue;
    }
    return color;
  }
}

final Widget svgIcon = SvgPicture.string(svgString, colorMapper: const _MyColorMapper());

Red becomes blue and every other colour is left alone.

An SVG that uses currentColor has no surrounding text colour to take in an app. The package’s SvgTheme supplies one, black by default, and the named constructors such as SvgPicture.asset accept a theme argument: theme: const SvgTheme(currentColor: Colors.teal).

SVG icons in buttons

SvgPicture is a widget, so it goes wherever a widget is accepted. IconButton takes any widget as its icon:

IconButton(
  onPressed: () {},
  icon: SvgPicture.asset(
    'assets/icons/save.svg',
    width: 24,
    height: 24,
    colorFilter: const ColorFilter.mode(Colors.teal, BlendMode.srcIn),
    semanticsLabel: 'Save',
  ),
)

Before shipping icons, make each file as plain as it can be: one viewBox, no editor data, shapes with colours written as attributes. The SVG optimizer strips editor data in the browser.

Backgrounds and decorations

SvgPicture cannot be given to a BoxDecoration. A DecorationImage needs an ImageProvider, and SvgPicture is a widget, not an image provider. To put an SVG behind other content, layer the two with a Stack:

Stack(
  children: [
    Positioned.fill(
      child: SvgPicture.asset('assets/background.svg', fit: BoxFit.cover),
    ),
    const Center(child: Text('In front')),
  ],
)

Loading, errors and screen readers

ArgumentWhat it does
placeholderBuilderBuilds a widget to show while the SVG is fetched and parsed. Without it the space is an empty box
errorBuilderBuilds a widget to show when loading fails. It receives the context, the error and the stack trace
semanticsLabelThe description read out by screen readers
excludeFromSemanticsSet to true for decoration, to hide the picture from screen readers

The README of 2.3.0 says there is currently no way to show an error visually, but the source disagrees with it. The changelog lists errorBuilder under version 2.0.17, and SvgPicture passes the builder to the vector_graphics package, which does the drawing. In that package’s source, read at version 1.2.3, a failed load is caught and handed to errorBuilder, and when none is given the placeholder stays on screen. The tests shipped with flutter_svg 2.3.0 exercise it with an invalid string, a missing asset and a missing file.

For an asset that does not exist, the README says error messages are printed to the console in debug mode. A blank space where a picture should be is therefore a reason to read the debug output, or to add an errorBuilder that shows the error.

What SVG it can draw

flutter_svg does not hand the file to a browser engine. It parses the SVG with the vector_graphics_compiler package, whose README lists what is covered:

SupportedNot supported
Groups, paths and the basic shapesFilters
References, including ones to elements defined laterSome text processing attributes
Linear and radial gradients
Text, symbols, images and patterns

So a drop shadow or blur made with an SVG <filter> will not appear. CSS is the other gap. The flutter_svg README, in its advice on exporting from Adobe Illustrator, says to choose Presentation Attributes and not Inline CSS “because CSS is not fully supported”, and to embed images in the file and not link them. A file whose colours live in a <style> block is therefore a likely cause of shapes that come out black. SVG and CSS explains the difference between attributes and style sheets, and exporting SVG from Illustrator covers that dialogue.

To test a file before building the app, the README gives a command that runs the compiler and reports any errors:

dart run vector_graphics_compiler -i $SVG_FILE -o $TEMPORARY_OUTPUT_TO_BE_DELETED --no-optimize-masks --no-optimize-clips --no-optimize-overdraw --no-tessellate

Precompiling

The same compiler can turn an SVG into a binary file ahead of time, which the README describes as faster to parse:

dart run vector_graphics_compiler -i assets/foo.svg -o assets/foo.svg.vec

The result is loaded with the default constructor and a loader from the vector_graphics package:

import 'package:vector_graphics/vector_graphics.dart';

const Widget svg = SvgPicture(AssetBytesLoader('assets/foo.svg.vec'));

Since version 2.2.0 there is also a renderingStrategy argument. The default, RenderingStrategy.picture, keeps the drawing as vectors. RenderingStrategy.raster draws it once into an image and reuses that, which the documentation recommends considering for very large or complicated graphics shown at a fixed scale, at the cost of sharpness if the widget is scaled afterwards.

Common problems

What you seeCauseFix
An empty space, and an error about the asset in the consoleThe file is not listed under assets: in pubspec.yaml, the path differs, or the indentation is wrongList the file or its folder; match the path exactly
Image.asset will not show an .svgSVG is not among the formats Image listsSvgPicture.asset
Shapes are black, or colours are missingThe file styles its shapes with CSSExport with presentation attributes
A shadow or blur is missingSVG filters are not supportedDraw the effect with Flutter widgets, or use a PNG for that picture
A linked picture inside the SVG is missingThe SVG points at another fileEmbed the image in the SVG
The whole picture turned one colourcolorFilter with BlendMode.srcIn replaces every colourRemove the filter, or use colorMapper
The layout jumps as pictures loadNo size givenSet width and height
An SVG will not go in a BoxDecorationIt is a widget, not an ImageProviderStack

Questions

Does Flutter support SVG without a package? Not through Image. The package is the Flutter team’s own answer: pub.dev shows flutter.dev as its publisher.

Which platforms does it run on? pub.dev lists Android, iOS, Linux, macOS, web and Windows for flutter_svg 2.3.0.

Should I convert icons to PNG instead? An SVG stays sharp at every screen density from one file, where PNG needs several sizes. For a picture that depends on filters or CSS, a PNG is the safer route: SVG to PNG renders one at any size.

Is this the same as react-native-svg? No. That is the equivalent library for React Native: see SVG in React Native.