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:
| Constructor | Takes | Use for |
|---|---|---|
SvgPicture.asset(name) | The asset’s path | Files shipped with the app |
SvgPicture.network(url) | A URL | Files fetched from a server |
SvgPicture.string(text) | SVG markup as a String | SVG built in code or received from an API |
SvgPicture.file(file) | A File | Files on the device |
SvgPicture.memory(bytes) | A Uint8List | SVG 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
| Argument | Meaning | Default |
|---|---|---|
width, height | The size of the widget | The size of its parent |
fit | How the picture is fitted into that space | BoxFit.contain |
alignment | Where the picture sits when there is room to spare | Alignment.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
| Argument | What it does |
|---|---|
placeholderBuilder | Builds a widget to show while the SVG is fetched and parsed. Without it the space is an empty box |
errorBuilder | Builds a widget to show when loading fails. It receives the context, the error and the stack trace |
semanticsLabel | The description read out by screen readers |
excludeFromSemantics | Set 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:
| Supported | Not supported |
|---|---|
| Groups, paths and the basic shapes | Filters |
| References, including ones to elements defined later | Some 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 see | Cause | Fix |
|---|---|---|
| An empty space, and an error about the asset in the console | The file is not listed under assets: in pubspec.yaml, the path differs, or the indentation is wrong | List the file or its folder; match the path exactly |
Image.asset will not show an .svg | SVG is not among the formats Image lists | SvgPicture.asset |
| Shapes are black, or colours are missing | The file styles its shapes with CSS | Export with presentation attributes |
| A shadow or blur is missing | SVG filters are not supported | Draw the effect with Flutter widgets, or use a PNG for that picture |
| A linked picture inside the SVG is missing | The SVG points at another file | Embed the image in the SVG |
| The whole picture turned one colour | colorFilter with BlendMode.srcIn replaces every colour | Remove the filter, or use colorMapper |
| The layout jumps as pictures load | No size given | Set width and height |
An SVG will not go in a BoxDecoration | It is a widget, not an ImageProvider | Stack |
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.