flutter_svg icon indicating copy to clipboard operation
flutter_svg copied to clipboard

Provide easy migration example(s) for `precachePicture`

Open Dohmanlechx opened this issue 2 years ago • 23 comments

I just updated this package to 2.0.0+1 and noticed how precachePicture no longer existed in the API.

Removed the affected functions from the "root page" of the app and yes, now my bottom navigation bar icons and the main logo are delayed. Didn't have this problem when I could precache the images.

I have found this: https://github.com/dnfield/flutter_svg/blob/master/vector_graphics.md#precachepicture but unfortunately, it didn't tell me anything. Please provide proper code examples of how to precache an SVG resource, would be highly appreciated.

Dohmanlechx avatar Feb 03 '23 10:02 Dohmanlechx

@Dohmanlechx Maybe this would work.

const loader = SvgAssetLoader('res/large_icon.svg');
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));

hygehyge avatar Feb 03 '23 17:02 hygehyge

  1. Are your icons still delayed in release/profile mode? See #837 - in debug mode things are slower right now.
  2. I can add something about precaching. The problem ends up being that people precache at "bad" times like startup, which delays whole app startup. It shouldn't be quite as bad with the new version because of isolates, but it's still not great.

The code that @hygehyge provided should work as well.

dnfield avatar Feb 04 '23 00:02 dnfield

  1. No, it's much better in profile mode! 🎉 The small icons in the bottom navigation bar load instantly, but the large SVG illustration we have on the main page still is a bit delayed. I wrote a similar comment here: https://github.com/dnfield/flutter_svg/issues/840#issuecomment-1416062838
  2. Actually, we prefer like 500 ms slower app startup than letting our end users see a jumpy layout when the big illustration eventually pops up. That isn't good UX. It would be so nice if you added them back, but I fully understand your concern and motive. I will try to convince our designer to sacrifice this big illustration to get a slightly faster app startup.

@Dohmanlechx Maybe this would work.

const loader = SvgAssetLoader('res/large_icon.svg');
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));

Worked like a charm! I really can't feel any difference in the app startup either. Thank you! @dnfield Feel free to close this issue anytime, but I believe this code example would help a lot of people.

Dohmanlechx avatar Feb 04 '23 11:02 Dohmanlechx

  1. Are your icons still delayed in release/profile mode? See I am seeing a significant performance regression on 2.0.0 #837 - in debug mode things are slower right now.
  2. I can add something about precaching. The problem ends up being that people precache at "bad" times like startup, which delays the whole app startup. It shouldn't be quite as bad with the new version because of the isolates, but it's still not great.

The code that @hygehyge provided should work as well.

The color parameter seems to be deprecated, Please what's the replacement for color SVGs?

levi956 avatar Feb 06 '23 13:02 levi956

@Dohmanlechx Maybe this would work.

const loader = SvgAssetLoader('res/large_icon.svg');
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));

This seems like a single asset file, is it possible to add an asset path for the whole svg path and place it in void main startup?

levi956 avatar Feb 06 '23 13:02 levi956

The color parameter seems to be deprecated, Please what's the replacement for color SVGs?

I switched to using colorFilter by passing in my previous colors into a ColorFilter like this: colorFilter: ColorFilter.mode(_color, BlendMode.srcIn),

Looks like that is basically the default when you don't pass into anything for colorFilter and it's working for me.

swiftymf avatar Feb 06 '23 18:02 swiftymf

  1. Are your icons still delayed in release/profile mode? See I am seeing a significant performance regression on 2.0.0 #837 - in debug mode things are slower right now.

This was exactly my case. I was running debug builds and it was slow. Once I found this I tried a release build and it's looking good. 👍

swiftymf avatar Feb 06 '23 18:02 swiftymf

@hygehyge is there a reason you don't include the context, and instead pass null?

simplenotezy avatar Feb 22 '23 10:02 simplenotezy

@levi956 You can iterate asset names like this.

final manifestJson = await rootBundle.loadString('AssetManifest.json')
final images = json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/images'));

@simplenotezy I use that code to load a large icon for startup screen, on entry point(void main()). So there is no BuildContext yet.

hygehyge avatar Feb 22 '23 14:02 hygehyge

Based on @hygehyge example I have implemented below in main before running app.

      final manifestJson = await rootBundle.loadString('AssetManifest.json');
      List svgsPaths = (json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/images/') && key.endsWith('.svg')) as Iterable).toList();

      for(var svgPath in svgsPaths as List<String>) {
        var loader = SvgAssetLoader(svgPath);
        await svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
      }

and this is how I am getting svg instance.

  static SvgPicture get getSomeImage => SvgPicture.asset(
        "assets/images/some_image.svg",
        height: 40,
        width: 40,
        color: Colors.white,
      );
      ```

sikandernoori avatar Mar 15 '23 10:03 sikandernoori

What if I use ".svg.vec" files as described in "Precompiling and Optimizing SVGs" section in README in flutter_svg? Should I cache these files and if yes then how?

olegyablokov avatar Apr 12 '23 15:04 olegyablokov

You should not need to cache the .vec files.

dnfield avatar Apr 12 '23 16:04 dnfield

      final manifestJson = await rootBundle.loadString('AssetManifest.json');
      List svgsPaths = (json.decode(manifestJson).keys.where((String key) => key.startsWith('assets/images/') && key.endsWith('.svg')) as Iterable).toList();

      for(var svgPath in svgsPaths as List<String>) {
        var loader = SvgAssetLoader(svgPath);
        await svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
      }

This code snippet provided by @sikandernoori used to work for me, but all of a sudden, I'm facing this issue below:

Unhandled Exception: 'package:vector_graphics_codec/vector_graphics_codec.dart': Failed assertion: line 771 pos 12: 'format == 0': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2      VectorGraphicsCodec.writeImage (package:vector_graphics_codec/vector_graphics_codec.dart:771:12)
#3      _encodeInstructions (package:vector_graphics_compiler/vector_graphics_compiler.dart:171:11)
#4      encodeSvg (package:vector_graphics_compiler/vector_graphics_compiler.dart:142:10)
#5      SvgLoader._load.<anonymous closure>.<anonymous closure> (package:flutter_svg/src/loaders.dart:137:14)
#6      _testCompute (package:flutter_svg/src/utilities/compute.dart:12:38)
#7      SvgLoader._load.<anonymous closure> (package:flutter_svg/src/loaders.dart:135:21)

keithhie avatar Apr 17 '23 11:04 keithhie

Hi, any expectation about this?

felipecastrosales avatar Aug 23 '23 19:08 felipecastrosales

FYI: I see my bottom navigator SVGs "blinking" when the main screen opens, the same behavior we see with asset images. My bet is not on this library's performance, but on the time that takes from assets to be retrieved from storage.

I am on Flutter 3.10.7 and I can't see this on iOS, only on Android.

feinstein avatar Sep 06 '23 00:09 feinstein

@feinstein Yesterday I also noticed this in my app, precisely in my bottom navigation - like you. In fact, in a part of my app I decided to migrate the assets to png and perform a precacheImage and the difference in performance was absurd - much better.

felipecastrosales avatar Sep 06 '23 12:09 felipecastrosales

There are other ways of solving this:

1 - Precache SVGs as described here.

2 - Create a dart file where each of your SVGs are a String. Since they will be part of your code, they don't need to be loaded from storage.

3 - Transform your SVGs into a font and use them as Icons (that's they way Flutter Icons work).

feinstein avatar Sep 06 '23 12:09 feinstein

You should not need to cache the .vec files.

@dnfield I'm using the compiled .vec images and still experience a delay before the image renders

SvgPicture(AssetBytesLoader("assets/logo_lights_large.svg.vec"), height: 50),

DanMossa avatar Dec 06 '23 20:12 DanMossa

Based on @hygehyge example I have implemented below in main before running app

I have found a way to do the same without AssetManifest.json file:

Future<void> precacheSvgImages() async {
  final assetManifest = await AssetManifest.loadFromAssetBundle(rootBundle);
  final assets = assetManifest.listAssets();

  // the same code below
  final svgPaths = assets.where((path) => path.endsWith('.svg'));
  for (final svgPath in svgPaths) {
    final loader = SvgAssetLoader(svgPath);
    await svg.cache
        .putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
  }
}

This function has to be called inside of initState() method of MainApp for example—it will not work in the main() before runApp started.

eshfield avatar Dec 13 '23 10:12 eshfield

There are other ways of solving this:

1 - Precache SVGs as described here.

2 - Create a dart file where each of your SVGs are a String. Since they will be part of your code, they don't need to be loaded from storage.

3 - Transform your SVGs into a font and use them as Icons (that's they way Flutter Icons work).

For 3. I don't think fonts can have multiple colors can they ?

For 2. That behavior is different for the web for multiple reasons

cedvdb avatar Feb 03 '24 09:02 cedvdb

Never tested it on the Web, could you elaborate more on why it doesn't work in the web?

feinstein avatar Feb 04 '24 01:02 feinstein

assets are dwnloaded separately. Http2 parallelism and browser caching are some of the things you give up on the web by putting the svg inside your dart code.

cedvdb avatar Feb 04 '24 12:02 cedvdb

Good point, but unless you have lots of SVGs, or huge ones, this shouldn't be a problem in today's internet speeds, since SVGs are tiny.

feinstein avatar Feb 04 '24 12:02 feinstein