Flutter FutureBuilder rebuilds unnecessarily when navigate between screens

1k Views Asked by At

My FutureBuilder rebuilds unnecessarily when navigate between screens. Each time I have to get the download URL from the Firebase Storage and this results in extremely flickering. How can I prevent that the FutureBuilder rebuilds everytime I navigate between screens?

I already tried the following solutions:

https://medium.com/saugo360/flutter-my-futurebuilder-keeps-firing-6e774830bc2

Flutter Switching to Tab Reloads Widgets and runs FutureBuilder

But without success. With both the FutureBuilder get always rebuild.

This is my code (Attempt 1):

class _GuestbookCardImageState extends State<GuestbookCardImage> {
  final AsyncMemoizer _memoizer = AsyncMemoizer();

  _getFeaturedImages() {
    return this._memoizer.runOnce(() async {
      return await MyStorage().getDownloadUrl(widget.guestbook.imagePath);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      semanticContainer: true,
      clipBehavior: Clip.antiAliasWithSaveLayer,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10.0),
      ),
      elevation: 2,
      margin: EdgeInsets.all(5),
      child: FutureBuilder(
          future: _getFeaturedImages(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return GuestbookPlaceholder();
            } else {
              if (snapshot.data != null) {
                return Hero(
                  tag: "to_single_guestbook_" + widget.guestbook.entryCode,
                  child: Container(
                    width: Get.width / 1.7,
                    height: Get.height / 3.5,
                    child: Image.network(
                      snapshot.data,
                      fit: BoxFit.cover,
                    ),
                  ),
                );
              } else {
                return Hero(
                    tag: "to_single_guestbook_" + widget.guestbook.entryCode,
                    child: GuestbookPlaceholder());
              }
            }
          }),
    );
  }
}

Attempt 2

class _GuestbookCardImageState extends State<GuestbookCardImage> {
  Future<String> _future;

  @override
  void initState() {
    _future = MyStorage().getDownloadUrl(widget.guestbook.imagePath);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      semanticContainer: true,
      clipBehavior: Clip.antiAliasWithSaveLayer,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(10.0),
      ),
      elevation: 2,
      margin: EdgeInsets.all(5),
      child: FutureBuilder(
          future: _future,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return GuestbookPlaceholder();
            } else {
              if (snapshot.data != null) {
                return Hero(
                  tag: "to_single_guestbook_" + widget.guestbook.entryCode,
                  child: Container(
                    width: Get.width / 1.7,
                    height: Get.height / 3.5,
                    child: CachedNetworkImage(
                        imageUrl: snapshot.data, fit: BoxFit.cover),
                  ),
                );
              } else {
                return Hero(
                    tag: "to_single_guestbook_" + widget.guestbook.entryCode,
                    child: GuestbookPlaceholder());
              }
            }
          }),
    );
  }
}

What I expect

I expect that after changing the screen and return to the initial screen my images still appearing without the need of reload. I am using Hero animation for the images. This loading behavior destroys my animation because while loading the image urls it shows a placeholder.

A small video how this looks: (And this happens also if I push to the detail screen and pop back to the initial screen)

enter image description here

What could I do to solve this issue?

2

There are 2 best solutions below

6
On BEST ANSWER

I think you need to go higher in the widget tree and preserve the state of your page. you can try this solution. The solution is to use PageView widget to display your screen through the navigation bar, and the PageView allowing you to save the page state easily.

0
On

What helped me was setting the memCacheWidth property to be screen width x devicePixelRatio, so the image is cached in the highest resolution that will ever be necessary.

Note: if you want to do zooming, consider doing width x 10 (or some other number which works for your case).

CachedNetworkImage(
      imageUrl: url,
      memCacheWidth: mediaQuery.size.width.ceil() * mediaQuery.devicePixelRatio.ceil(),
      // ... other properties ...
),