Hero widget working only when going back in Flutter
1 min read

Hero widget working only when going back in Flutter

I stumbled across a weird bug in one of the apps I am working on: the hero animation was not visible on navigating to a new route, but it was present when popping the route to go back to the initial page.

I checked that the Hero widget was set up properly. The tag was the same, the widget subtree was exactly the same, below the Hero widget, and I was using MaterialApp, which makes the Hero transition work automagically.

It turned out that an ancestor of the Hero was a StreamBuilder, and it had no initialData. As explained in the documentation, the framework first calculates where the Hero will be in the destination route, but it does that with the initialData of the StreamBuilder, which, if not provided, is null.

So, code like this, in the destination page, does not work:

StreamBuilder<String>(
      stream: imageService.url,
      builder: (context, snapshot) {
        final url = snapshot.data;
        if (url == null)
          return CircularProgressIndicator();
        else
          return Hero(
              tag: "image",
              Image.network(url),
          ); 
      }
),

because, when the framework checks the position of the Hero in the new screen, the StreamBuilder as an initial data of null, and the progress indicator should be shown, not the Image widget.

When we go back to the source screen from the destination one, the framework already knows the emitted value for the previous screen, and the Hero animation is shown correctly.

The solution is to add initialData to the StreamBuilder:

StreamBuilder<String>(
 	  initialData: defaultUrl,
      stream: imageService.url,
      builder: (context, snapshot) {
        final url = snapshot.data;
        if (url == null)
          return CircularProgressIndicator();
        else
          return Hero(
              tag: "image",
              Image.network(url),
          ); 
      }
),

where defaultUrl must either be a placeholder value or is presumably obtained synchronously higher in the widget tree.

Now the Hero animation works both when pushing the destination route and when popping it.

Subscribe to a curated newsletter

Receive an email every week with curated content about Dart and Flutter.

See previous issues of the newsletter.