Skip to content
Blog

SVG in Flutter: Rendering Vector Graphics with flutter_svg

A practical guide to rendering SVG in Flutter with flutter_svg: assets, network and string sources, color filters, pre-caching, performance tuning, and when to reach for vector_graphics_compiler instead.

Published on • October 4, 2026

AI Assistant

SVG in Flutter: Rendering Vector Graphics with flutter_svg

Shipping a raster PNG for every icon is one of those quietly expensive habits. A hero illustration exported at 3x eats hundreds of kilobytes, still looks soft on a device you did not design for, and needs a second asset for dark mode. An SVG solves all three problems at once: it is resolution-independent, typically a few kilobytes, and you can recolor it at runtime. Flutter does not ship a built-in SVG renderer, but the flutter_svg package — now maintained by the Flutter team itself and published as a Flutter Favorite — fills that gap cleanly.

In this post you will learn how to load SVGs from assets, the network, and raw strings, how to tint and map colors, and how to keep parsing off your frame budget.

Key technologies: flutter_svg 2.3.0, vector_graphics, vector_graphics_compiler, dart:ui Canvas, ColorFilter, ColorMapper.

Why SVG on Mobile

Vector graphics describe shapes as mathematical paths rather than a grid of pixels. That gives you three practical wins on a phone:

  1. Scalability without asset fan-out. One file renders crisply at 24dp in a list, at 96dp in a detail header, and at any device pixel ratio — no mdpi–xxxhdpi folders, no @2x/@3x variants.
  2. Tiny files. A typical UI icon SVG is 1–5 KB compared to 20–80 KB for a PNG of the same icon at a single density. Multiply that by a few hundred icons and the install-size difference is real.
  3. Runtime theming. Colors are just attributes in the file, so you can recolor an icon for light/dark themes or brand states without shipping duplicate assets.

The trade-off is parse cost: SVG is XML, and turning it into something the GPU can draw takes CPU time. We will come back to that in the performance section.

Installing flutter_svg

Add the dependency to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flutter_svg: ^2.3.0

Then run flutter pub get. Recent versions of flutter_svg sit on top of the vector_graphics family of packages (vector_graphics, vector_graphics_codec, vector_graphics_compiler), where the parsing and rendering work actually happens. Assets referenced through pubspec.yaml need the usual declaration:

flutter:
  assets:
    - assets/icons/

Loading SVGs: Asset, Network, and String

SvgPicture has convenience constructors for the three sources you will actually encounter.

From an asset — the default, and cheapest since nothing is fetched at runtime:

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

From the network — useful for user-supplied or server-driven graphics. Use placeholderBuilder so the layout does not jump while the file downloads and parses:

final Widget networkSvg = SvgPicture.network(
  'https://example.com/illustrations/shark.svg',
  semanticsLabel: 'Shark illustration',
  placeholderBuilder: (BuildContext context) => Container(
    padding: const EdgeInsets.all(30.0),
    child: const CircularProgressIndicator(),
  ),
);

From a string — handy when the SVG comes from an API response, a constant in your code, or an editor the user configured:

const rawSvg = '''
<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 svgIcon = SvgPicture.string(rawSvg);

By default the placeholder is an empty LimitedBox. If you set width or height, the package swaps in a SizedBox instead, giving you a more predictable layout while loading. Note that there is currently no visual error widget: a missing or malformed file is logged to the console in debug mode and gives you an empty box in release. If you need graceful failure, validate the asset in a widget test.

Sizing, Fit, and Color

SvgPicture behaves like an Image widget for layout purposes. You can constrain it with width/height and control how the vector’s viewBox maps to that box with fit:

SvgPicture.asset(
  'assets/illustrations/hero.svg',
  width: 240,
  height: 160,
  fit: BoxFit.contain,
);

For tinting, reach for colorFilter. The srcIn blend mode is the standard trick for making a single-color icon adopt any color:

SvgPicture.asset(
  'assets/icons/warning.svg',
  colorFilter: const ColorFilter.mode(Colors.red, BlendMode.srcIn),
  semanticsLabel: 'Warning icon',
);

When you need finer control than a single tint — say, remapping hardcoded brand colors — extend ColorMapper and override substitute. The callback receives the color plus element and attribute context, so the mapping can be arbitrary:

class ThemeColorMapper extends ColorMapper {
  const ThemeColorMapper();

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

// ...
SvgPicture.string(rawSvg, colorMapper: const ThemeColorMapper());

This is the cleanest path to dark-mode support for SVGs that were not designed with currentColor.

Pre-caching and Performance

Parsing an SVG is XML work: the file is decoded, the structure is built, and it is compiled into a Picture the renderer can replay. Doing that on the main isolate during a route transition is a reliable way to drop frames — especially for complex illustrations with many paths.

The package ships a pre-cache API on top of package:vector_graphics so you can pay that cost ahead of time:

import 'package:vector_graphics/flutter/vector_graphics.dart';

Future<void> warmUpIcons() async {
  for (final asset in ['assets/icons/home.svg', 'assets/icons/search.svg']) {
    await preloadPicture(
      AssetBytesLoader(asset),
      ServicesBinding.rootBuildContext,
    );
  }
}

Call something like this during your app’s splash or first-frame setup. SvgPicture also caches parsed pictures internally, so a SVG reused across a list does not re-parse per row — but the first render still does, which is exactly what pre-caching smooths over.

A few more practical notes:

  • Prefer assets over network for anything on a hot path. Network SVGs pay a fetch plus a parse.
  • Keep files small. Round-trip exports through SVGO-style optimizers; strip editor metadata, unused <defs>, and invisible layers.
  • Consider the raster render strategy. The default picture mode keeps full scaling flexibility, while raster mode renders the SVG into an Image first and draws that — significantly faster for static, fixed-size graphics, at the cost of resolution-scaling flexibility.

Precompiling with vector_graphics_compiler

For the best parse performance, skip XML parsing at runtime. vector_graphics_compiler converts an SVG into a compact binary format (.svg.vec) that is faster to load and lets the compiler eliminate clipping, masking, and overdraw:

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

Load it with the base SvgPicture constructor:

import 'package:vector_graphics/vector_graphics.dart';

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

You can also run the compiler locally as a compatibility check — if it throws on your file, the runtime backend will struggle too. Wire it into your build as a small script or Gradle/Xcode step, and designers keep editing normal SVGs while the app ships the fast binary.

Alternatives: When Not to Use flutter_svg

flutter_svg is the default for static vector art, but not the only option:

  • Rive and Lottie — if your vectors need to animate, reach for rive or lottie instead. Trying to hand-roll animation on top of static SVGs is a losing game.
  • Icon fonts — for simple UI glyphs, Flutter’s Icon widget is cheaper than any SVG and inherits theme colors for free. Do not wrap a Material Symbol in an SVG.
  • Image.asset with WebP — for photorealistic or heavily-filtered artwork where vectors do not help.

Gotchas and Unsupported Features

The vector graphics backend targets SVG 1.1 but does not implement every corner of the spec. The common failure modes:

  • CSS is not fully supported. Prefer presentation attributes over inline CSS — the package docs explicitly recommend Presentation Attributes instead of Inline CSS in Adobe Illustrator’s SVG export options.
  • External references break. Linked images, external fonts, and cross-file <use xlink:href> will not resolve. Embed resources so each SVG is self-contained.
  • Some filters and blend modes are missing. Heavy feTurbulence-style raster effects may render differently than in a browser. Test on a real device, not just in your design tool.- Missing assets fail silently. A typo in the path gives you an empty box in release builds. Guard it with a test.

When a file misbehaves, run it through vector_graphics_compiler first — the error output usually points straight at the offending element.

Wrapping Up

flutter_svg gives Flutter a production-grade SVG story: three load sources, theming hooks through ColorFilter and ColorMapper, pre-caching for smooth transitions, and an optional compiler pipeline for when parse cost matters. Pair it with a disciplined, optimizer-cleaned export workflow and you can delete most of your PNG density variants — and still ship sharper UI.

Sources