With the help of PageRouteBuilder
we can create our own transition animation of the pages. We know that to show a page we need the following statement:
Navigator.push(context, MaterialPageRoute(builder: (context) => NoteBooks()));
Here the push
method takes a Route
object as the second parameter. The class MaterialPageRoute
creates a route object and returns it. This class is derived from PageRoute
, and PageRoute
is derived from ModalRoute
.
There are three class derived from PageRoute
and they are MaterialPageRoute
, CupertinoPageRoute
and PageRouteBuilder
.
A PageRoute
is a modal route that replaces the entire screen.
The MaterialPageRoute
creates a PageRoute
object based on the given page, and then adds transition to it. CupertinoPageRoute
does the similar thing but it adds different style animation (iOS style). The class PageRouteBuilder
has the ability to create PageRoute
and adding transition to the page but a developer must provide the transition effect.
So, we can extend the class PageRouteBuilder
to create our own transition effect. We can also use the constructor. So lets first create a custom transition using the constructor:
The constructor has a required parameter pageBuilder
. This takes a callback. The callback should return the page that we are going to push.
Navigator.push(context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation){ return NewPage(); } ));
The animation
parameter is a Animation<double>
that generate values between 0 and 1. You can use this parameter to create transition.
Navigator.push(context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation){ return FadeTransition( opacity: animation, child: NoteBooks(), ); } ));
The above code will animate the page which fades in. To customize the duration you can pass transitionDuration
property:
Navigator.push(context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation){ return FadeTransition( opacity: animation, child: NoteBooks(), ); }, transitionDuration: Duration(seconds: 2), ));
The constructor takes another parameter which is transitionsBuilder
, this takes a callback. The purpose of this parameter is to seperate the transition part from the main Page construction. Thus, the pageBuilder
should be only responsible for creating the page and transitionsBuilder
should be responsible for adding the transition effect to the page.
Navigator.push(context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation){ return NoteBooks(); }, transitionsBuilder: (context, animation, secondaryAnimation, child){ return FadeTransition( opacity: animation, child: child, ); }, transitionDuration: Duration(seconds: 2), ));
The method pageBuilder
runs first and it returns the page widget. Then it calls the transitionsBuilder
callback and passes the returned page from the pageBuilder
as child
parameter. You can then use the child
parameter to incorporate within the transition widget. As both callback receives animation
and secondaryAnimation
parameter we can build the transition in either of the builder method. It is more effecient to built the transition part within the pageBuilder
as the page is generally contained in seperate file and we only uses the constructor to build the page.
Both the callback receives secondaryAnimation
parameter. This is a Animation<double> animation. We can use this controller to show animation when another another page pushes on top of this one. This animation is run in the following two cases:
Navigator.push(context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation){ return Test(); }, transitionsBuilder: (context, animation, secondaryAnimation, child){ Animation<double> ani = Tween<double>(begin: 1, end: 0).animate(secondaryAnimation); return FadeTransition( opacity: animation, child: ScaleTransition( scale: ani, child: child, ), ); }, ));
You can extend the class PageRouteBuilder
to create a custom PageRoute
object. The class should accept a parameter child
which is the actual page. In the constructor you must call super
and pass the required parameter pageBuilder
.
The class PageRouteBuilder
defines two metehod, pageBuilder
and transitionsBuilder
. You can override them to customize the transition. Following is an example:
import 'package:flutter/material.dart'; class BeautyTransition extends PageRouteBuilder{ Widget child; BeautyTransition(this.child) : super( pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation){ return child; }, transitionDuration: Duration(milliseconds: 70), ); @override buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child){ Animation<double> scaleAnimation = Tween<double>(begin: 0.6, end: 1.0).animate(CurvedAnimation( parent: animation, curve: Interval(0.0, 0.8, curve: Curves.easeIn), ) ); Animation<double> opacityAnimation = Tween<double>(begin: 0.0, end: 1).animate( CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: Curves.easeIn), ), ); return ScaleTransition( scale: scaleAnimation, child: FadeTransition( opacity: opacityAnimation, child: child ) ); } }
In the above example, we have initialize the parent constructor using the following:
BeautyTransition(this.child) : super( pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation){ return child; }, transitionDuration: Duration(milliseconds: 70), );
In the above constructor the required parameter pageBuilder
takes a callback and it just returns the child and does nothing else. Most of the time its sufficient for parent constructor to be like this. We have also passed transitionDuration
parameter to parent constructor to override the default duration of the animation. The parameter transitioniDuration
is optional.
Next, we are overriding the method buildTransitions
, this is where our custom transition will be defined. You can create any animation you want and return the animated version of the Page from this method.
Note that we have not defined buildPage
method. As it is not necessary. If you define this method then it will override the pageBuilder
in the super constructor. As there is no customization needed for pages, the method buildPage
won't do anything extra. It is rarely defined.
@override buildPage(BuildConext context, Animation<double> animation, Animation<double> secondaryAnimation){ return child; }
Just like in the constructor you can also define the animation within the pageBuild
method. Like the following:
import 'package:flutter/material.dart'; class BeautyTransition extends PageRouteBuilder{ Widget child; BeautyTransition(this.child) : super(pageBuilder: (context, animation, secondaryAnimation)=> child); @override buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation){ Animation<double> scaleAnimation = Tween<double>(begin: 0.6, end: 1.0).animate(CurvedAnimation( parent: animation, curve: Interval(0.0, 0.8, curve: Curves.easeIn), ) ); Animation<double> opacityAnimation = Tween<double>(begin: 0.0, end: 1).animate( CurvedAnimation( parent: animation, curve: Interval(0.0, 1.0, curve: Curves.easeIn), ), ); return ScaleTransition( scale: scaleAnimation, child: FadeTransition( opacity: opacityAnimation, child: child ) ); } }
As you can see you can use either of the method to define the transition. But the initialization of parent constructor is must.
Now you can use the custom class in Navigator
call like following:
Navigator.push(context, BeautyTransition(NewPage()));