HeroModifier class

A widget that marks its child as being a candidate for hero animations.

When a PageRoute is pushed or popped with the Navigator, the entire screen's content is replaced. An old route disappears and a new route appears. If there's a common visual feature on both routes then it can be helpful for orienting the user for the feature to physically move from one page to the other during the routes' transition. Such an animation is called a hero animation. The hero widgets "fly" in the Navigator's overlay during the transition and while they're in-flight they're, by default, not shown in their original locations in the old and new routes.

To label a widget as such a feature, wrap it in a Hero widget. When navigation happens, the Hero widgets on each route are identified by the HeroController. For each pair of Hero widgets that have the same tag, a hero animation is triggered.

If a Hero is already in flight when navigation occurs, its flight animation will be redirected to its new destination. The widget shown in-flight during the transition is, by default, the destination route's Hero's child.

For a Hero animation to trigger, the Hero has to exist on the very first frame of the new page's animation.

Routes must not contain more than one Hero for each tag.

{@tool dartpad} This sample shows a Hero used within a ListTile.

Tapping on the Hero-wrapped rectangle triggers a hero animation as a new MaterialPageRoute is pushed. Both the size and location of the rectangle animates.

Both widgets use the same Hero.tag.

The Hero widget uses the matching tags to identify and execute this animation.

** See code in examples/api/lib/widgets/heroes/hero.0.dart ** {@end-tool}

{@tool dartpad} This sample shows Hero flight animations using default tween and custom rect tween.

** See code in examples/api/lib/widgets/heroes/hero.1.dart ** {@end-tool}

Discussion

Heroes and the Navigator's Overlay Stack must be axis-aligned for all this to work. The top left and bottom right coordinates of each animated Hero will be converted to global coordinates and then from there converted to that Stack's coordinate space, and the entire Hero subtree will, for the duration of the animation, be lifted out of its original place, and positioned on that stack. If the Hero isn't axis aligned, this is going to fail in a rather ugly fashion. Don't rotate your heroes!

To make the animations look good, it's critical that the widget tree for the hero in both locations be essentially identical. The widget of the target is, by default, used to do the transition: when going from route A to route B, route B's hero's widget is placed over route A's hero's widget. Additionally, if the Hero subtree changes appearance based on an InheritedWidget (such as MediaQuery or Theme), then the hero animation may have discontinuity at the start or the end of the animation because route A and route B provides different such InheritedWidgets. Consider providing a custom flightShuttleBuilder to ensure smooth transitions. The default flightShuttleBuilder interpolates MediaQuery's paddings. If your Hero widget uses custom InheritedWidgets and displays a discontinuity in the animation, try to provide custom in-flight transition using flightShuttleBuilder.

By default, both route A and route B's heroes are hidden while the transitioning widget is animating in-flight above the 2 routes. placeholderBuilder can be used to show a custom widget in their place instead once the transition has taken flight.

During the transition, the transition widget is animated to route B's hero's position, and then the widget is inserted into route B. When going back from B to A, route A's hero's widget is, by default, placed over where route B's hero's widget was, and then the animation goes the other way.

Nested Navigators

If either or both routes contain nested Navigators, only Heroes contained in the top-most routes (as defined by Route.isCurrent) of those nested Navigators are considered for animation. Just like in the non-nested case the top-most routes containing these Heroes in the nested Navigators have to be PageRoutes.

Parts of a Hero Transition

Diagrams with parts of the Hero transition.

Inheritance
Available extensions

Constructors

HeroModifier({Key? key, Key? modifierKey, Widget? child, required Object tag, CreateRectTween? createRectTween, HeroFlightShuttleBuilder? flightShuttleBuilder, HeroPlaceholderBuilder? placeholderBuilder, bool transitionOnUserGestures = false})
Create a hero.
const

Properties

createRectTween CreateRectTween?
Defines how the destination hero's bounds change as it flies from the starting route to the destination route.
final
flightShuttleBuilder HeroFlightShuttleBuilder?
Optional override to supply a widget that's shown during the hero's flight.
final
hashCode int
The hash code for this object.
no setterinherited
key Key?
Controls how one widget replaces another widget in the tree.
finalinherited
modifierKey Key?
The actual key of the widget, which Modifier wrapped
finalinherited
placeholderBuilder HeroPlaceholderBuilder?
Placeholder widget left in place as the Hero's child once the flight takes off.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
tag Object
The identifier for this particular hero. If the tag of this hero matches the tag of a hero on a PageRoute that we're navigating to or from, then a hero animation will be triggered.
final
transitionOnUserGestures bool
Whether to perform the hero transition if the PageRoute transition was triggered by a user gesture, such as a back swipe on iOS.
final

Methods

build(BuildContext context) Widget
Describes the part of the user interface represented by this widget.
inherited
buildWithChild(BuildContext context, Widget? child) Widget
A build method that receives an extra child parameter.
override
createElement() SingleChildStatelessElement
Create a SingleChildStatelessElement
inherited
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
modified() Modifier

Available on Widget, provided by the ModifierTransformer extension

Transform normal widget to Modifier
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object.
inherited
toStringShort() String
A short, textual description of this widget.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited