Load phone's gallery as fast as native IOS in Flutter without OOM

293

You can use a logic like this :

final Map<String, Uint8List?> _cachedMap = {};
void precacheAssets(int index) async {
    // Handle cache before index
    for (int i = max(0, index - 50); i < 50; i++) {
      getItemAtIndex(i);
    }
    // Handle cache after index
    for (int i = min(assetsList.length, index + 50); i < 50 + min(assetsList.length, index + 50); i++) {
      getItemAtIndex(i);
    }
    _cachedMap.removeWhere((key, value) {
      int currIndex = assetsList.indexWhere((element) => element.id == key);
      return currIndex < index - 50 && currIndex > index + 50;
    });
  }
  /// Get the asset from memory or fetch it if it doesn’t exist yet.
  /// Called in the builder method to display assets, not to precache them.
  Future<Uint8List?> getItemAtIndex(int index) async {
    AssetEntity entity = assetsList[index];
    if (_cachedMap.containsKey(entity.id)) {
      return _cachedMap[entity.id];
    }
    else {
      Uint8List? thumb = await entity.thumbDataWithOption(
          ThumbOption.ios(
              width: width,
              height: height,
              deliveryMode: DeliveryMode.highQualityFormat,
              quality: 90));
      _cachedMap[entity.id] = thumb;
      return thumb;
    }
  }

And you can call the precacheAssets method in your GridView.builder at a specific index for example if (index % 25 == 0) which will tell every 25 items, put in cache the 50 next ones so it will add 25 more items to the existing cache.

Also, call the getItemAtIndex in your Future.builder as future param and you will get instantly the asset if it’s in memory, otherwise load it as usual.

Feel free to change the values and test it, it’s already improved with these values in my iPhone but if you are scrolling VERY fast you will still see as before a bit.

You can add a FadeTransition in this case which will result in a non-ugly UI.

Share:
293
Tom3652
Author by

Tom3652

Updated on January 01, 2023

Comments

  • Tom3652
    Tom3652 over 1 year

    I am trying to load the phone's gallery (with pagination) in a GridView.builder widget.

    Here is the issue i have created using the photo_manager package.
    I have got some help and it made me think about a possible solution (see my last comment on the issue).

    I would like to be able to load the assets without blinking or white page.
    On IOS native it's veeery fast and smooth, i want to achieve the same in Flutter.

    You will find all the pieces of code i have made in the github link above. I have managed to do so using a Map object in memory but i need to improve the algorithm to not be in OOM.

    Solutions wanted (one or the other) :

    • A simple way to do this, load the phone's gallery as fast as the native IOS into a GridView, no matter which package is used the time it's working.
    • An improvement of my currently poor algorithm that would keep for example the 15 assets above the current one, 15 assets below in memory and during the scroll, keep updating these values to move the range around the current position in the list.

    Please let me know if this is not clear enough, as a reminder please have a look at my last big comment on this issue.

    • Tom3652
      Tom3652 over 2 years
      Before asking to close because it "needs more focus", please specify in the comment what is not clear and i will update the question. The use case is very simple, i just need the phone's gallery to be loaded as fast as the IOS native gallery.
    • Omatt
      Omatt over 2 years
      If you just need to create an image gallery as an image picker, have you considered using image_picker plugin?
    • Tom3652
      Tom3652 over 2 years
      I need to customize the Gallery. This means i have to build a custom UI page that will load the items + other UI specifications. image_picker works well but it opens a new Intent / new page that is not customizable. If i am wrong, please feel free to write an answer.
  • Tom3652
    Tom3652 over 2 years
    Thanks for taking time to reply. However it doesn't work as expected. Have you checked my github issue ? I am already using thumbnails even 64x64 to test and it doesn't work. I have actually also provided a begining of solution on the Github. With yours, i am seeing the same as i have requested on Github :( I have checked your code, and you are using the same methods as i am and items take too much time to be loaded in the GridView unfortunately :/. What i need is a fluent user experience without empty screens for few milliseconds when scrolling very fast.
  • Andrey Gordeev
    Andrey Gordeev over 2 years
    Then you need to precache thumbnails in the background after the page is opened, using album.getAssetListRange
  • Tom3652
    Tom3652 over 2 years
    You are right and that's what i have started to do, this question is about an implementation of this solution actually, working with 5k + assets and precaching only 20-30 maximum ahead (scrolling up + down) to minimize memory load and avoid OOM.
  • Tom3652
    Tom3652 over 2 years
    Thanks for your answer. Since it still doesn't load as fast as native IOS perfectly, i don't set it as an accepted answer yet in case someone finds a better way, but i am tuning a bit your example and i have something really satisfying !