Baseflow / flutter_cached_network_image

Download, cache and show images in a flutter app
https://baseflow.com
2.44k stars 656 forks source link

Performance compared to Image #730

Open Sesa1988 opened 2 years ago

Sesa1988 commented 2 years ago

🏗 Performance issue

I have performance issues with using this package in a list.

CachedNetworkImage(
          imageUrl: _imageUrl,
          fit: BoxFit.fitWidth,
          height: 40,
          width: 40,
          errorWidget: (context, url, error) =>
              const SizedBox(width: 40, height: 40, child: AppLogo(40, 40)),
          placeholder: (context, url) => const SizedBox(width: 40, height: 40),
        )

Sadly this results in pretty high raster ms/frame, about 40-80 even without scrolling.
If I'm using Image or FadeInImage.memoryNetwork, I'm getting raster ms/frame about 9-11 and lower around 7ms UI thread.

FadeInImage.memoryNetwork(
            image: _imageUrl,
            fit: BoxFit.fitWidth,
            placeholder: kTransparentImage,
            placeholderErrorBuilder: (_, __, ___) => const SizedBox(
              width: 40,
              height: 40,
              child: AppLogo(
                40,
                40,
                useSmallImage: true,
              ),
            ),
          )

Is there any option to improve this? It's laggy on the first time and even after caching the images its not comparable.

Sesa1988 commented 2 years ago

I don't know if this helps but I tested it with using the cache manager directly for similar performance increases, this just flickers sometimes on Hero animations for example.

I can provide an example repo the next week, if needed.

class TestCachedImage extends StatefulWidget {
  final String url;
  final double size;
  final BoxFit? fit;

  const TestCachedImage(
    this.url, {
    this.size = 40,
    this.fit,
  });

  @override
  State<TestCachedImage> createState() => _TestCachedImageState();
}

class _TestCachedImageState extends State<TestCachedImage> {
  File? file;
  bool isShowPlacerholder = false;

  @override
  Future<void> didChangeDependencies() async {
    super.didChangeDependencies();
    var temp = await DefaultCacheManager().getFileFromMemory(widget.url);
    if (temp == null) {
      var fileInfo = await DefaultCacheManager().downloadFile(
        widget.url,
        key: widget.url,
      );
      var tempFile = fileInfo.file;
      if (await tempFile.exists()) {
        if (mounted) {
          setState(() {
            file = tempFile;
          });
        }
      } else {
        if (mounted) {
          setState(() {
            isShowPlacerholder = true;
          });
        }
      }
    } else {
      if (mounted) {
        setState(() {
          file = temp.file;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    if (isShowPlacerholder) {
      return AppLogo(widget.size, widget.size, useSmallImage: true);
    }
    if (file == null) return SizedBox(width: widget.size, height: widget.size);

    return Image.file(
      file!,
      width: widget.size,
      height: widget.size,
      fit: widget.fit,
    );
  }
}
slavap commented 2 years ago

@Sesa1988 Are you using builder in you ListView or rendering all items at once?

Sesa1988 commented 2 years ago

@Sesa1988 Are you using builder in you ListView or rendering all items at once?

I'm using ListView.builder, its independent of my implementation, with an normal Image I'm getting 3ms/frame on the UI thread. I did already a lot of optimization's around it.

slavap commented 2 years ago

@Sesa1988 The strange thing is that I have quite an opposite experience from yours. I'm using mostly Flutter Web, maybe that's the reason, but in my case cached_network_image improves the performance of the ListView with large images.

Sesa1988 commented 2 years ago

@Sesa1988 The strange thing is that I have quite an opposite experience from yours. I'm using mostly Flutter Web, maybe that's the reason, but in my case cached_network_image improves the performance of the ListView with large images.

I dont know about flutter web but for mobile (tested on android) its for sure worse. I have a widget where I can decide what widget I want to render in my list. The variants described above basically.

I will upload an example this week when I have some free time.

w568w commented 2 years ago

Experienced the same poor performance with this library, 1-2x lower frame rates (Compared to a simple NetworkImage) on desktop and mobile devices, sometimes 100~500 ms/frame with many images. Especially when scrolling very fast through a long list, it stucks and crashes then, in some worst cases.

After some profiling work, I believe the lagging has something to do with fading animation of the image views and the cache & I/O scheduling. Even when the widget is invisible, the image seems to be eagerly loaded somehow.

chihuy105 commented 1 year ago

Any solution so far?

Fantome5 commented 1 year ago

+1 here appreciate any workarounds.

kobars commented 7 months ago

I can confirm this issue, I experienced a drop in frame rate when using a big list with list view or other pagination view libraries. The issue disappeared when I switched to NetworkImage.

Experienced the same poor performance with this library, 1-2x lower frame rates (Compared to a simple NetworkImage) on desktop and mobile devices, sometimes 100~500 ms/frame with many images. Especially when scrolling very fast through a long list, it stucks and crashes then, in some worst cases.

After some profiling work, I believe the lagging has something to do with fading animation of the image views and the cache & I/O scheduling. Even when the widget is invisible, the image seems to be eagerly loaded somehow.

richanshah commented 4 months ago

Any update on this issue ?

justprodev commented 2 months ago

try set fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero

richanshah commented 2 months ago

I am using cache manager singlton object and it works better now.

class CacheManagerSingleton {
  // Private static instance of CacheManager
  static final CacheManager _cacheManager = CacheManager(
    Config(
      'uniqueCacheKey', // Unique key for cache manager
      stalePeriod: const Duration(days: 7),
      maxNrOfCacheObjects: 100,
      repo: JsonCacheInfoRepository(databaseName: 'cacheDatabase'),
    ),
  );

  // Private named constructor
  CacheManagerSingleton._internal();

  // Factory constructor to return the singleton instance
  factory CacheManagerSingleton() {
    return _instance;
  }

  // Private static instance of CacheManagerSingleton
  static final CacheManagerSingleton _instance = CacheManagerSingleton._internal();

  // Getter for the CacheManager instance
  CacheManager get cacheManager => _cacheManager;
}