MipZ
MipZ

Reputation: 3

Hero with CachedNetworkImage

I have a list of speakers. When I click on any of the items, I navigate to a page with details about that speaker. During the transition, a small avatar transforms into a large one using the Hero widget. These avatars are CachedNetworkImages. In the normal state (when the image is successfully loaded from the network), everything works fine. However, if you simulate a situation where the image does not load, or another error occurs, the errorWidget of the CachedNetworkImages begins to flicker during the transition.

GIF of how it looks now: https://imgur.com/a/E7ogQhV

(Sorry, For some unknown reason, I can't load this through the site.)

I tried fetching the image myself and, depending on the response code, choosing to display either the placeholder widget or the CachedNetworkImages. However, a different behavior arose, where the placeholder widget was displayed at the beginning even if the image had already been successfully loaded.

class NetworkImageWidget extends StatefulWidget {
  const NetworkImageWidget({
    super.key,
    required this.imageUrl,
    this.width,
    this.height,
    this.fit = BoxFit.cover,
  });

  final String? imageUrl;
  final double? width;
  final double? height;
  final BoxFit? fit;

  @override
  State<NetworkImageWidget> createState() => _NetworkImageWidgetState();
}

class _NetworkImageWidgetState extends State<NetworkImageWidget> {
  @override
  void initState() {
    super.initState();
    // fetchImage();
  }

  bool isImageReal = false;

  bool get _isImageInvalid =>
      (widget.imageUrl?.isEmpty ?? true) ||
      (!widget.imageUrl!.startsWith('http') || !isImageReal);

  // Future<void> fetchImage() async {
  //
  //   final response = await http.get(Uri.parse(widget.imageUrl!));
  //   if (response.statusCode == 200) {
  //     setState(() => isImageReal = true);
  //   } else {
  //     setState(() => isImageReal = false);
  //   }
  // }

  @override
  Widget build(BuildContext context) {
    // if (_isImageInvalid) {
    //   return _PlaceHolderImageWidget(
    //     width: widget.width,
    //     height: widget.height,
    //     fit: widget.fit,
    //   );
    // }

    return CachedNetworkImage(
      imageUrl: widget.imageUrl!,
      width: widget.width,
      height: widget.height,
      fit: widget.fit,
      // cacheManager: CacheManager,
      placeholder: (_, __) => _CircleImageShimmer(
        width: widget.width,
        height: widget.height,
      ),
      errorWidget: (_, __, ___) => _PlaceHolderImageWidget(
        width: widget.width,
        height: widget.height,
        fit: widget.fit,
      ),
    );
  }
}

class _CircleImageShimmer extends StatelessWidget {
  const _CircleImageShimmer({
    required this.width,
    required this.height,
  });

  final double? width;
  final double? height;

  @override
  Widget build(BuildContext context) {
    final appColor = AppColorScheme.of(context);
    return Shimmer.fromColors(
      baseColor: appColor.shimmer,
      highlightColor: appColor.shimmerHighlight,
      child: Container(
        color: Colors.white,
        width: width,
        height: height,
      ),
    );
  }
}

class _PlaceHolderImageWidget extends StatelessWidget {
  const _PlaceHolderImageWidget({this.width, this.height, this.fit});

  final double? width;
  final double? height;
  final BoxFit? fit;

  @override
  Widget build(BuildContext context) => Image.asset(
        Images.largePlaceholder,
        width: width,
        height: height,
        fit: fit,
      );
}

This is my widget code for the CachedNetworkImages, which I then wrap in a Hero widget.

Upvotes: 0

Views: 69

Answers (0)

Related Questions