Scalable Vector Graphics (SVG) is one of the most widely used file image formats in applications. Because it offers several advantages over bitmap files, especially when it comes to retaining image quality while scaling, it’s difficult to start building a Flutter application without first considering using SVG.
In this article, you’ll learn how and when to use SVG files in a Flutter application.
Skia, a 2D graphics library and core component of Flutter, can only serialize images into SVG files. Thus, decoding or rendering SVG images with Skia is not possible. Fortunately, the community comes to the rescue with a fantastic Dart-native package called flutter_svg that renders and decodes SVG quickly.
The flutter_svg package has implemented a picture cache that stores a ui:Picture
class. This is the SkPicture
wrapper of the Skia graphics engine, which records specific SVG rendering commands in binary mode.
The ui.Picture
class does not consume much memory and is cached to avoid repeatedly parsing XML files. Let’s take a look at the SvgPicture.asset
constructor:
SvgPicture.asset( String assetName, { Key? key, this.matchTextDirection = false, AssetBundle? bundle, String? package, this.width, this.height, this.fit = BoxFit.contain, this.alignment = Alignment.center, this.allowDrawingOutsideViewBox = false, this.placeholderBuilder, Color? color, BlendMode colorBlendMode = BlendMode.srcIn, this.semanticsLabel, this.excludeFromSemantics = false, this.clipBehavior = Clip.hardEdge, this.cacheColorFilter = false, }) : pictureProvider = ExactAssetPicture( allowDrawingOutsideViewBox == true ? svgStringDecoderOutsideViewBox : svgStringDecoder, assetName, bundle: bundle, package: package, colorFilter: svg.cacheColorFilterOverride ?? cacheColorFilter ? _getColorFilter(color, colorBlendMode) : null, ), colorFilter = _getColorFilter(color, colorBlendMode), super(key: key);
By looking at the implementation, you’ll notice that the stream notifications from pictureProvider
update the picture of SvgPicture
.
void _resolveImage() { final PictureStream newStream = widget.pictureProvider .resolve(createLocalPictureConfiguration(context)); assert(newStream != null); // ignore: unnecessary_null_comparison _updateSourceStream(newStream); }
In this code block, the stream from the pictureProvider
is populated with ui.Picture
by a completer of the picture cache.
PictureStream resolve(PictureConfiguration picture, {PictureErrorListener? onError}) { // ignore: unnecessary_null_comparison assert(picture != null); final PictureStream stream = PictureStream(); T? obtainedKey; obtainKey(picture).then<void>( (T key) { obtainedKey = key; stream.setCompleter( cache.putIfAbsent( key!, () => load(key, onError: onError), ), ); }, ).catchError((Object exception, StackTrace stack) async { if (onError != null) { onError(exception, stack); return; } FlutterError.reportError(FlutterErrorDetails( exception: exception, stack: stack, library: 'SVG', context: ErrorDescription('while resolving a picture'), silent: true, // could be a network error or whatnot informationCollector: () sync* { yield DiagnosticsProperty<PictureProvider>( 'Picture provider', this); yield DiagnosticsProperty<T>('Picture key', obtainedKey, defaultValue: null); })); }); return stream; }
To add this package to your Flutter dependencies, you can run:
flutter pub add flutter_svg
Alternatively, you can add flutter_svg to your pubspec.yaml
file:
dependencies: flutter_svg: ^0.22.0
Make sure that you run flutter pub get
either in your terminal or using your editor. Once installation is complete, import the package in your Dart code where you want to use this package:
import 'package:flutter_svg/flutter_svg.dart';
There are several ways to use this package, but we’ll cover the most common use cases.
One option is to load an SVG from an internal SVG file, which is typically stored in the asset
folder:
// example final String assetName = 'assets/image.svg'; final Widget svg = SvgPicture.asset( assetName, );
You can also load an SVG file from an URL, like so:
// example final Widget networkSvg = SvgPicture.network( 'https://site-that-takes-a-while.com/image.svg', );
Finally, you can load an SVG from an SVG code:
// example SvgPicture.string( '''<svg viewBox="...">...</svg>''' );
Once you have your SVG file loaded, the first step is to change the color or tint of the image:
// example final String assetName = 'assets/up_arrow.svg'; final Widget svgIcon = SvgPicture.asset( assetName, color: Colors.red, );
Using a semantics label helps to describe the purpose of the image and enhances accessibility. To achieve this, you can add the semanticsLabel
parameter. The semantic label will not be shown in the UI.
// example final String assetName = 'assets/up_arrow.svg'; final Widget svgIcon = SvgPicture.asset( assetName, color: Colors.red, semanticsLabel: 'A red up arrow' );
The flutter_svg package renders an empty box, LimitedBox
, as the default placeholder if there are no height
or width
assignments on the SvgPicture
. However, if a height
or width
is specified on the SvgPicture
, a SizedBox
will be rendered to ensure a better layout experience.
The placeholder can be replaced, though, which is great for improving the user experience, especially when loading assets via a network request where there may be a delay.
// example 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()), );
In this example, I’ve chosen CircularProgressIndicator
to display a progress indicator while the image is loading. You may add any other widget that you wish. For example, you might use a custom loading widget to replace CircularProgressIndicator
.
You should know that the flutter_svg library does not support all SVG features. The package does, however, provide a helper method to ensure that you don’t render a broken image due to a lack of supported features.
// example final SvgParser parser = SvgParser(); try { parser.parse(svgString, warningsAsErrors: true); print('SVG is supported'); } catch (e) { print('SVG contains unsupported features'); }
Please note that the library currently only detects unsupported elements like the <style>
tag, but does not recognize unsupported attributes.
To use make the most use of flutter_svg with Adobe Illustrator, you need to follow specific recommendations:
There may be times where you’d like to render your SVG into another canvas. SVGPicture
helps you easily achieve this:
// example final String rawSvg = '''<svg viewBox="...">...</svg>'''; final DrawableRoot svgRoot = await svg.fromSvgString(rawSvg, rawSvg); final Picture picture = svgRoot.toPicture(); svgRoot.scaleCanvasToViewBox(canvas); svgRoot.clipCanvasToViewBox(canvas); svgRoot.draw(canvas, size);
Using SVG files can be a great addition to your Flutter app, but SVG isn’t always the right answer to all of your image problems. It’s crucial to observe your use cases and measure your app and SVG performance continuously, as you might need to replace SVG with another standard image format, such as PNG or JPEG.
Although Flutter doesn’t support SVG natively, the Dart-native flutter_svg package has excellent performant and fast support for SVG files. The package is relatively simple to use, too.
Keep in mind that the package version is still below 1.0.0, which can break changes in API. However, the author has done a great job of keeping the API as stable as possible. When using flutter_svg, make sure you always check the last version of the package on pub.dev to stay up to date. Thanks for reading!
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.