Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching Image on flutter web, is it required?

Due to CachedNetworkImage not working on flutter web, upon porting, I have tried to use this, but my question is do we really need this? Or we just use Image. Network and the browser and service worker will handle the cache part (which is then set by server's response header through, for example, cache-control= "max-age=43200, public"

This is used on the food delivery project I am working on, https://www.santaiyamcha.com

Below are the classes I use to replace CachedNetworkImage which doesn't seem to work well.

import 'package:flutter/material.dart';
import 'package:http/http.dart';

import 'package:http_extensions_cache/http_extensions_cache.dart';
import 'package:http_extensions/http_extensions.dart';

/// Builds a widget when the connectionState is none and waiting
typedef LoadingBuilder = Widget Function(BuildContext context);

/// Builds a if some error occurs
typedef ErrorBuilder = Widget Function(BuildContext context, Object error);

class MeetNetworkImage extends StatelessWidget {
  /// Image url that you want to show in your app.
  final String imageUrl;

  /// When image data loading from the [imageUrl],
  /// you can build specific widgets with [loadingBuilder]
  final LoadingBuilder loadingBuilder;

  /// When some error occurs,
  /// you can build specific error widget with [errorBuilder]
  final ErrorBuilder errorBuilder;

  final double scale;

  final double width;
  final double height;
  final Color color;

  final FilterQuality filterQuality;

  final BlendMode colorBlendMode;

  final BoxFit fit;
  final AlignmentGeometry alignment;

  final ImageRepeat repeat;

  final Rect centerSlice;
  final bool matchTextDirection;

  /// Whether to continue showing the old image (true), or briefly show nothing
  /// (false), when the image provider changes.
  final bool gaplessPlayback;

  final String semanticLabel;
  final bool excludeFromSemantics;

  MeetNetworkImage({
    @required this.imageUrl,
    this.loadingBuilder = null,
    this.errorBuilder = null,
    this.scale = 1.0,
    this.height,
    this.width,
    this.color = const Color(0xFDFFFF),
    this.fit = BoxFit.fill,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.semanticLabel,
    this.centerSlice,
    this.colorBlendMode,
    this.excludeFromSemantics = false,
    this.filterQuality = FilterQuality.low,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
  })  : assert(imageUrl != null),
        assert(alignment != null),
        assert(repeat != null),
        assert(matchTextDirection != null);

  Future<Response> getUrlResponse() {
    /*
    //The caching part  I tried, does not seems working
    final client = ExtendedClient(
      inner: Client(),
      extensions: [
        CacheExtension(
          //logger: Logger("Cache"),
          defaultOptions: CacheOptions(
            expiry: const Duration(hours: 168),
            // The duration after the cached result of the request will be expired.
            //forceUpdate: false, // Forces to request a new value, even if an valid cache is available
            //forceCache: false, // Forces to return the cached value if available (even if expired).
            //ignoreCache: true, //Indicates whether the request should bypass all caching logic
            //returnCacheOnError: true, //If [true], on error, if a value is available in the  store if is returned as a successful response (even if expired).
            keyBuilder: (request) => "${request.method}_${imageUrl.toString()}",
            // Builds the unqie key used for indexing a request in cache.
            store: MemoryCacheStore(),
            // The store used for caching data.
            shouldBeSaved: (response) =>
                response.statusCode >= 200 && response.statusCode < 300,
          ),
        )
      ],
    );

    return client.get(imageUrl);
     */
    return get(imageUrl);
  }

  Widget getLoadingWidget(BuildContext context) {
    if (loadingBuilder != null) {
      return loadingBuilder(context);
    } else
      return Container(
          height: height, width: width,
          child: Center(
            child: CircularProgressIndicator()
          )
          /*Image.asset(
          'assets/img/loading4.gif',
          height: height,
          width: width,
          fit: BoxFit.contain,
        ),*/
          );
  }

  Widget getErrorWidget(BuildContext context, String error) {
    if (errorBuilder != null) {
      return errorBuilder(context, error);
    } else
      return Center(child: Icon(Icons.error));
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: getUrlResponse(),
      builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
            return getLoadingWidget(context);
          case ConnectionState.active:
          case ConnectionState.done:
            if (snapshot.hasError)
              return getErrorWidget(context, snapshot.error);
            if (!snapshot.hasData)
              return getErrorWidget(context, snapshot.error);
            //return getLoadingWidget(context);
            return Image.memory(
              snapshot.data.bodyBytes,
              scale: scale,
              height: height,
              width: width,
              color: color,
              fit: fit,
              alignment: alignment,
              repeat: repeat,
              centerSlice: centerSlice,
              colorBlendMode: colorBlendMode,
              excludeFromSemantics: excludeFromSemantics,
              filterQuality: filterQuality,
              gaplessPlayback: gaplessPlayback,
              matchTextDirection: matchTextDirection,
              semanticLabel: semanticLabel,
            );
        }
        return Container();
      },
    );
  }
}

What do you suggest?

like image 382
Orange Avatar asked Apr 13 '20 11:04

Orange


2 Answers

I am using FadeInImage.memoryNetwork. It is working fine. The browser handles the cache part.

like image 108
Xihuny Avatar answered Nov 20 '22 13:11

Xihuny


No. This is an issue with Google Chrome which prefers "cache-control" over E-Tags or Last Modified Headers. In my case I am using Firefox which caches on the basis of E-Tags. So here is the thing.

If you are using setState((){}) or the flutter engine calls the setState due to some reason, the images are rebuilt and if the images are not cached, it is again fetched. To prevent it, use the header cache-control: max-age=<any value greater than 0> and it will work fine in Chrome.

Or just build the app using web renderer canvaskit - flutter build web --web-renderer canvaskit.

I have come to this conclusion based on my experience and I could not find it anywhere, Hope it helps :)

like image 42
Shivam Singh Avatar answered Nov 20 '22 13:11

Shivam Singh