Flutter Tutorial – #1.11 – HERO Animation | Page Transition
Introduction
Welcome to Himdeve development, where we are preparing the best tutorials to make your mobile app development easier and more efficient.
Goal
- We will create a Hero animation to navigate between pages
- We will add special animation and transition effects for Hero animation
- We will learn how to set Ripple Effect over the entire widget, including its children
- We will learn how to define our own Builder using Typedef
- We will add caching to our images loaded from the Internet
Procedure
First we open our existing application from the previous tutorial and we open the portfolio_tutorials_sub_page.dart file. This will be our entry file for this tutorial.
We have a list of tutorials defined here using the Tuple2 class.
Where the first item represents the url address to the tutorial image and the second represents the title or description of the tutorial.
Hero animation
Hero refers to a widget that flies between screens. Hero animation is therefore the animation of the widget, most often a picture, from one screen to another during page transition using a Page Route navigation.
In our case we want to use Hero animation between the item from our tutorial list and the corresponding detail for that item.
hero_widget.dart
Let’s start right away by creating a Hero Widget class. We create a new hero_widget.dart file in the components folder. It will be a Stateless Widget – HeroWidget.
Typedef
We define Typedef Hero Builder.
The Typedefs are aliases for functions that are objects. The usual use of Typedef aliases is a callback interface. In our case, HeroBuilder is an alias for a function that takes BuildContext as a parameter and returns a Widget.
We define the heroBuilder variable into the HeroWidget class.
We will add heroBuilder to the constructor of this class as a required attribute, but since the builder of a callback function is usually called just a builder, we set the name of this parameter to the builder.
We will also define other parameters for this class. In order to identify and pair 2 Hero widgets, we have to set the same Hero Tag for both of them. So we add the heroTag parameter. It is also useful to allow adjusting the size of the Hero widget by defining its width. To do this, we use the width parameter. And as the last parameter, we set the onTap callback method, which will respond to tapping the widget.
HeroWidget – build method
Then we define the build method of this widget.
To be able to set a size of the Hero widget, we need to wrap it with the SizedBox widget, which we can set the width we define, using the width attribute.
SizedBox has another child attribute where we insert the Hero widget.
Hero
Hero has an attribute tag that, as mentioned, is used to identify and pair two Hero widgets. We’ll set our heroTag variable here.
We insert the InkWell widget into the child attribute of the Hero widget, which allows us to define the Ripple effect. We explained the Ripple effect in detail in the previous tutorial. InkWell has a child attribute, in which we insert our heroBuilder callback function with the context of the build method. We put the void callback method – onTap into the onTap attribute.
The InkWell widget is wrapped with a Material widget with a transparent color so that the Ripple effect has a place where it can be displayed.
RotationTransition
Another attribute of the Hero widget is flightShuttleBuilder, which is used to change the transition layer between the two destinations of the Hero widget.
Let’s go back to RotationTransition, a transition that animates the rotation of the widget.
RotationTranstion has attribute turns, to control the rotation of its child. We’ll put a Tween here. Tween represents a linear interpolation between start and end values. In our case, this will be the number of times that widget will rotate during the transition between pages.
The advantage of Tweens is that they can be linked together using a chain command. Therefore, we can add an acceleration of the widget at the beginning of the animation and a slowdown at the end with the Curves.ease call to the object rotation. This call is set to the curve attribute for the CurveTween class.
Finally, we call the animate method that creates Tween‘s animation object.
Another attribute of the RotationTransition widget is the child into which we insert the contents of the Hero, specifically its subtree, by calling widget.child. From the destination context, we select the Hero, cast it to be recognizable as Hero, and then use the cascading operator or the symbol (..) to select it from its subtree. Double dots allow us to perform a sequence of operations on one object.
Thus we went through the whole hero_widget.dart file.
We go back to the portfolio_tutorials_sub_page.dart file.
Ripple Effect for the entire widget
To achieve the Ripple effect for the entire widget so that it also overlaps the images that are rendered in it, we need to use the Stack widget.
Therefore, in the _buildListItem method, we return a Stack widget with two children. The first child will be the _buildCardView method, which returns a Card widget wrapped in a Positioned widget. And the second child will be the _buildRippleEffectNavigation method, which creates the desired Ripple Effect across the entire widget list item.
In the _buildCardView method, we will return an existing Card widget, wrapped in a Positioned widget using the Positioned.fill constructor, which will stretch the widget to the parent’s width and height. By default, the top, bottom, left and right attributes are set to 0 here.
The Card widget has a Row widget inserted into the child attribute to set our HeroWidget (tutorial image) to the left side of the Row widget using the _buildHeroWidget method and the tutorial description to the right side of the Row widget using the _buildDesc method.
In the _buildHeroWidget method, we return HeroWidget, which we set the width to 150 using the width attribute. We set the image url to the heroTag attribute. The url address should represent a unique key. And in our builder attribute, that represents a callback function defined by the Typdef alias in the hero_widget.dart file, we return the contents for HeroWidget using the _buildHeroWidgetContent method.
In _buildHeroWidgetContent method, we return a CachedNetworkImage widget representing a preview of the tutorial.
In order to use the CachedNetworkImage widget, we must first define the cached_network_image library in the pubspec.yaml file.
The second method in Row Widget will be, next to HeroWidget, a description for the tutorial, created using the _buildDesc method.
In the _buildRippleEffectNavigation method, we return the Positioned widget using the Positioned constructor – Positioned.fill. We insert a Material widget with transparent color into the child attribute to give the Ripple Effect a space to render and we insert the InkWell widget into the child attribute of the Material widget. The InkWell widget has an onTap attribute to which we call the Navigator push method to change the page.
We define the new page in the _createTutorialDetailRoute method.
In case we do not want to change the animation speed of the page transition, we can use the classic MaterialPageRoute constructor to create a route to the detail of the Tutorial. However, we want to change the animation duration to 1 second and also to add some more transition’s effects. For this reason, we use the PageRouteBuilder constructor to create the path to the Tutorial.
transitionDuration
The PageRouteBuilder constructor has the transitionDuration attribute, in which we set the transition speed to 1 second by using the Duration object.
transitionsBuilder
With the MaterialPageRoute constructor, FadeTransition is automatically defined as we move from one page to another. However, using PageRouteBuilder, we must set a transitionsBuilder attribute to create the FadeTransition.
In case we want to add another Tween to FadeTransition, we can also use chaining, same as for Hero widget.
And if we want to connect FadeTransition with another transition, such as SlideTranstion, it is possible by inserting the next transition into the child attribute of the previous one.
pageBuilder
The last attribute we define for PageRouteBuilder will be pageBuilder, in which we call the class representing the new page, which in our case is a detail of the particular tutorial – PortfolioTutorialDetailPage.
PortfolioTutorialDetailPage
We create a file portfolio_tutorial_detail_page.dart in the pages/portfolio/ folder. Here we create a Stateless Widget – PortfolioTutorialDetailPage.
We define the required heroTag, desc, and imageUrl attributes.
PortfolioTutorialDetailPage – build metóda
And in the build method of this widget, we return the Scaffold widget, to which we define AppBar with the title ‘Tutorial Detail‘ to have a back button to be able to go back to the previous page.
We define a method to create the contents of the widget – _buildContent at attribute body.
In the _buildContent method, we want to display the HeroWidget at the top of the screen, which contains the image representing the tutorial, and below that its text, describing a particular tutorial. To do this we use the Column widget, which has the children attribute, where we first define the _buildHeroWidget method to create the HeroWidget and then the _buildDesc method to create the tutorial description. To make the content of our widget scrollable, the Column widget is wrapped with a SingleChildScrollView widget.
In the _buildHeroWidget method, we return the HeroWidget to which we set the heroTag attribute. We set the width to the full width of the screen using the MediaQuery.of(context).size.width command. And in our builder attribute, we return the content for HeroWidget using the _buildHeroWidgetContent method.
In the _buildHeroWidgetContent method, we will reuse the CachedNetworkImage widget, which represents a preview of the tutorial from the Internet.
In the _buildDesc method, we return the tutorial title using the Text widget. We center it using the textAlign attribute. And we set the font size to 30. Then we wrap this widget with an 8-size Padding widget.
If we want to slow down the running of the application in order to see the individual animations and effects in more detail, we can set the timeDilation setter method to the beginning of the build method.
Conclusion
And with this is our eleventh part of this first series of Flutter Tutorials completed and of course you can find the complete source code on the githube.