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.
Leave a comment