2d-inc / Flare-Flutter

Load and get full control of your Rive files in a Flutter project using this library.
https://rive.app/
MIT License
2.55k stars 469 forks source link

How to cache an asset globally #235

Open Nijinsha opened 4 years ago

Nijinsha commented 4 years ago

I have a .flr asset to show on the loading screen. I need it to be cached when the app loads. And access the cached asset anywhere in the app?

umberto-sonnino commented 4 years ago

If you want to warm up the cache before the app runs you can do this:

const _assetsToWarmup = [
  AssetFlare(bundle: rootBundle, name: "assets/file.flr")
];

Future<void> warmupFlare() async {
  for (final asset in _assetsToWarmup) {
    await cachedActor(asset);
  }
}

void main() {
  // Newer versions of Flutter require initializing widget-flutter binding
  // prior to warming up the cache.
  WidgetsFlutterBinding.ensureInitialized();

  // Don't prune the Flare cache, keep loaded Flare files warm and ready
  // to be re-displayed.
  FlareCache.doesPrune = false;

  // Warm the cache up.
  warmupFlare().then((_) {
    // Finally start the app.
    runApp(MyApp());
  });
}

This keeps all Flare files warmed up in cache via FlareCache.doesPrune = false;

Once your Flare cache has been warmed up, you can use the named constructor FlareActor.asset for building the widget with the appropriate asset.

dannyvalentesonos commented 4 years ago

@umberto-sonnino This sort of tripped me up, as we started seeing flashing in our app and it was because flares were suddenly getting pruned, so that when using them again they'd have to be warmed again. I couldn't really find any documentation on this and had to find it the hard way by debugging and finding out our flares were taken out of the cache suddenly, which led me to this ticket. What is the purpose of pruning the cache? Is it to save memory on the device?
If so, then I find that the design isn't very flexible. It's basically all or nothing for whether you want to keep flares warm. So, what is the point? I'd suggest that information on whether the asset should be pruned or not should be a property on the AssetProvider. This way you could decide which assets should never be pruned because they are important to keep warm, while others don't have to stay warm too and help with conserving memory. Thoughts?

Also, I found that trying to "pre-warm" the cache at load time can have a significant performance cost and in our case slows down Flutter Engine load times by 2 seconds on older devices with a debug build. On release builds, it can still be noticeable, though it's not 2 seconds, on older devices. This can be a problem for the Add2App scenario where the flutter engine only gets launched at the time of requesting it in the app. The reason for this performance cost after investigating it more is due to the fact that each flare asset that gets loaded spawns an isolate to finish loading the actor values. While this is a good thing, spawning isolates are pretty costly, and so doing it for every asset individually adds up. Do you think there'd be the possibility of loading everything up and running all that in one isolate so that we only incur the isolate cost one time? I imagine there could be a way to warm a list of assets and the plugin could make use of that to batch the loading process.

Anyway, just a thought. If you think it's possible, I could log another ticket for it.

Thanks!!

emilysmithson commented 4 years ago

@dannyvalentesonos I'm having the same issue. I have a lot of flare assets and it is taking ages to cache them all. Once they are all cached it all works really smoothly. Did you find a workaround? Thanks

dannyvalentesonos commented 4 years ago

Unfortunately no workaround @emilysmithson . The only thing is to not pre-cache, or only pre-cache ones that are critical. @umberto-sonnino any thoughts on my comments above?

emilysmithson commented 4 years ago

Thanks, @dannyvalentesonos

umberto-sonnino commented 4 years ago

@umberto-sonnino This sort of tripped me up, as we started seeing flashing in our app and it was because flares were suddenly getting pruned, so that when using them again they'd have to be warmed again. I couldn't really find any documentation on this and had to find it the hard way by debugging and finding out our flares were taken out of the cache suddenly, which led me to this ticket. What is the purpose of pruning the cache? Is it to save memory on the device? If so, then I find that the design isn't very flexible. It's basically all or nothing for whether you want to keep flares warm. So, what is the point? I'd suggest that information on whether the asset should be pruned or not should be a property on the AssetProvider. This way you could decide which assets should never be pruned because they are important to keep warm, while others don't have to stay warm too and help with conserving memory. Thoughts?

You raise some really interesting points @dannyvalentesonos! Pruning was meant to be used without pre-warm, in fact, all the examples with pre-warming also lower the flag to disable it. This design is not super-flexible, and adding a property on the AssetProvider is certainly a good idea!

Also, I found that trying to "pre-warm" the cache at load time can have a significant performance cost and in our case slows down Flutter Engine load times by 2 seconds on older devices with a debug build. On release builds, it can still be noticeable, though it's not 2 seconds, on older devices. This can be a problem for the Add2App scenario where the flutter engine only gets launched at the time of requesting it in the app. The reason for this performance cost after investigating it more is due to the fact that each flare asset that gets loaded spawns an isolate to finish loading the actor values. While this is a good thing, spawning isolates are pretty costly, and so doing it for every asset individually adds up. Do you think there'd be the possibility of loading everything up and running all that in one isolate so that we only incur the isolate cost one time? I imagine there could be a way to warm a list of assets and the plugin could make use of that to batch the loading process.

That's unfortunate, thanks for bringing this scenario to our attention! Loading from storage is always expensive, more so with Add2App, we'll be looking into it.

Regarding Isolates, having a single one would also be a nice improvement! There's also a flag to be set called useCompute that tells the framework to spawn a new isolate.

lukepighetti commented 3 years ago

I wish we had a way to provide a list of paths to pre-cache and have flare_flutter handle the rest, even when using the naive FlareActor with a path. We could pre-cache them at any time and dispose of them at any time. But this is a minor nit because the functionality is there and it works well.