flutter_svg
flutter_svg copied to clipboard
Provide easy migration example(s) for `precachePicture`
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 Maybe this would work.
const loader = SvgAssetLoader('res/large_icon.svg');
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
- Are your icons still delayed in release/profile mode? See #837 - in debug mode things are slower right now.
- 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.
- 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
- 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.
- 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.
- 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?
@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?
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.
- 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. 👍
@hygehyge is there a reason you don't include the context, and instead pass null?
@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.
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,
);
```
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?
You should not need to cache the .vec files.
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)
Hi, any expectation about this?
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 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.
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).
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),
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.
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
Never tested it on the Web, could you elaborate more on why it doesn't work in the web?
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.
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.