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
- 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
-
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}) → 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