What you’ll learn

Animate the transformation of a hero’s shape from circular to
rectangular while flying it from one screen to another.

The Hero widget in Flutter implements a style of animation
commonly known as shared element transitions or
shared element animations.

You’ve probably seen hero animations many times. For example, a screen displays
a list of thumbnails representing items for sale. Selecting an item flies it to
a new screen, containing more details and a “Buy” button. Flying an image from
one screen to another is called a hero animation in Flutter, though the same
motion is sometimes referred to as a shared element transition.

You might want to watch this one-minute video introducing the Hero widget:

This guide demonstrates how to build standard hero animations, and hero
animations that transform the image from a circular shape to a square shape
during flight.

Examples: This guide provides examples of each hero animation style at
the following links.

You can create this animation in Flutter with Hero widgets. As the hero animates
from the source to the destination route, the destination route (minus the hero)
fades into view. Typically, heroes are small parts of the UI, like images, that
both routes have in common. From the user’s perspective the hero “flies” between
the routes. This guide shows how to create the following hero animations:

Standard hero animations

A standard hero animation flies the hero from one route to a new route,
usually landing at a different location and with a different size.

The following video (recorded at slow speed) shows a typical example. Tapping
the flippers in the center of the route flies them to the upper left corner of a
new, blue route, at a smaller size. Tapping the flippers in the blue route (or
using the device’s back-to-previous-route gesture) flies the flippers back to
the original route.

Radial hero animations

In radial hero animation, as the hero flies between routes
its shape appears to change from circular to rectangular.

The following video (recorded at slow speed),
shows an example of a radial hero animation. At the start, a
row of three circular images appears at the bottom of the route.
Tapping any of the circular images flies that image to a new route
that displays it with a square shape.
Tapping the square image flies the hero back to
the original route, displayed with a circular shape.

Basic structure of a hero animation

What's the point?

Use two hero widgets in different routes but with matching tags to
implement the animation.

The Navigator manages a stack containing the app’s routes.

Pushing a route on or popping a route from the Navigator’s stack
triggers the animation.

The Flutter framework calculates a rectangle
tween
that defines the hero’s boundary as it flies from the source to
the destination route. During its flight, the hero is moved to
an application overlay, so that it appears on top of both routes.

Hero animations are implemented using two
Hero
widgets: one describing the widget in the source route,
and another describing the widget in the destination route.
From the user’s point of view, the hero appears to be shared, and
only the programmer needs to understand this implementation detail.

Note about dialogs:
Heroes fly from one PageRoute to another. Dialogs
(displayed with showDialog(), for example), use PopupRoutes,
which are not PageRoutes. At least for now,
you can’t animate a hero to a Dialog.
For further developments (and a possible workaround), watch this
issue.

Hero animation code has the following structure:

Define a starting Hero widget, referred to as the source
hero. The hero specifies its graphical representation
(typically an image), and an identifying tag, and is in
the currently displayed widget tree as defined by the source route.

Define an ending Hero widget, referred to as the destination hero.
This hero also specifies its graphical representation,
and the same tag as the source hero.
It’s essential that both hero widgets are created with
the same tag, typically an object that represents the
underlying data. For best results, the heroes should have
virtually identical widget trees.

Create a route that contains the destination hero.
The destination route defines the widget tree that exists
at the end of the animation.

Trigger the animation by pushing the destination route on the
Navigator’s stack. The Navigator push and pop operations trigger
a hero animation for each pair of heroes with matching tags in
the source and destination routes.

Flutter calculates the tween that animates the Hero’s bounds from
the starting point to the endpoint (interpolating size and position),
and performs the animation in an overlay.

The next section describes Flutter’s process in greater detail.

Behind the scenes

The following describes how Flutter performs the transition from one route to
another.

Before transition, the source hero waits in the source route’s widget tree. The
destination route does not yet exist, and the overlay is empty.

Pushing a route to the Navigator triggers the animation. At t=0.0, Flutter does
the following:

Calculates the destination hero’s path, offscreen, using the curved motion as
described in the Material motion spec. Flutter now knows where the hero ends
up.

Places the destination hero in the overlay, at the same location and size as
the source hero. Adding a hero to the overlay changes its Z-order so that it
appears on top of all routes.

Essential classes

The widget that flies from the source to the destination route.
Define one Hero for the source route and another for the
destination route, and assign each the same tag.
Flutter animates pairs of heroes with matching tags.

Uses the hero widget directly.
This more basic example, provided for your reference, isn’t
described in this guide.

What’s going on?

Flying an image from one route to another is easy to implement
using Flutter’s hero widget. When using MaterialPageRoute
to specify the new route, the image flies along a curved path,
as described by the Material Design motion
spec.

When the user taps the InkWell containing the source hero, the code creates
the destination route using MaterialPageRoute. Pushing the destination route
to the Navigator’s stack triggers the animation.

The Container positions the PhotoHero in the destination route’s top-left
corner, below the AppBar.

The onTap() method for the destination PhotoHero pops the Navigator’s
stack, triggering the animation that flies the Hero back to the original
route.

Use the timeDilation property to slow the transition while debugging.

Radial hero animations

What's the point?

A radial transformation animates a circular shape into a square
shape.

A radial hero animation performs a radial transformation while
flying the hero from the source route to the destination route.

MaterialRectCenter­Arc­Tween defines the tween animation.

Build the destination route using PageRouteBuilder.

Flying a hero from one route to another as it transforms from a circular shape
to a rectanglar shape is a slick effect that you can implement using Hero
widgets. To accomplish this, the code animates the intersection of two clip
shapes: a circle and a square. Throughout the animation, the circle clip (and
the image) scales from minRadius to maxRadius, while the square clip
maintains constant size. At the same time, the image flies from its position in
the source route to its position in the destination route. For visual examples
of this transition, see Radial transformation in the Material motion spec.

This animation might seem complex (and it is), but you can customize the
provided example to your needs. The heavy lifting is done for you.

Radial hero animation code

Each of the following examples demonstrates a radial hero animation.
This guide describes the first example.

Extends radial_hero_animaton by also animating the size of the
rectangular clip. This more advanced example,
provided for your reference, isn’t described in this guide.

Pro tip:
The radial hero animation involves intersecting a round shape with
a square shape. This can be hard to see, even when slowing
the animation with timeDilation, so you might consider enabling
the [debugPaintSizedEnabled][] flag during development.

What’s going on?

The following diagram shows the clipped image at the beginning
(t = 0.0), and the end (t = 1.0) of the animation.

The blue gradient (representing the image), indicates where the clip
shapes intersect. At the beginning of the transition,
the result of the intersection is a circular clip
(ClipOval).
During the transformation,
the ClipOval scales from minRadius to maxRadius while the
ClipRect
maintains a constant size.
At the end of the transition the intersection of the circular and
rectangular clips yield a rectangle that’s the same size as the hero
widget. In other words, at the end of the transition the image is no
longer clipped.

The Inkwell captures the tap gesture.
The calling function passes the onTap() function to the
Photo’s constructor.

During flight, the InkWell draws its splash on its first Material
ancestor.

The Material widget has a slightly opaque color, so the
transparent portions of the image are rendered with color.
This ensures that the circle-to-square transition is easy to see,
even for images with transparency.

The Photo class does not include the Hero in its widget tree.
For the animation to work, the hero
wraps the RadialExpansion widget.

RadialExpansion class

The RadialExpansion widget, the core of the demo, builds the
widget tree that clips the image during the transition.
The clipped shape results from the intersection of a circular clip
(that grows during flight),
with a rectangular clip (that remains a constant size throughout).

As the hero flies, its size changes and, because it constrains its child’s
size, the RadialExpansion widget changes size to match.

The RadialExpansion animation is created by two overlapping clips.

The example defines the tweening interpolation using
MaterialRectCenterArcTween. The default flight path for a hero animation
interpolates the tweens using the corners of the heroes. This approach affects
the hero’s aspect ratio during the radial transformation, so the new flight
path uses MaterialRectCenterArcTween to interpolate the tweens using the
center point of each hero.