Route Management and Custom Route Transition Animations
In the "Route Management" section, we mentioned that the Material component library provides a MaterialPageRoute
component, which uses platform-specific route transition animations. For example, on iOS, routes transition with a left-right slide, while on Android, they slide vertically. But what if we want to use the left-right transition on Android as well? A simple solution is to use the CupertinoPageRoute
directly, like this:
Navigator.push( context, CupertinoPageRoute( builder: (context) => PageB(), ), );
CupertinoPageRoute
is a route transition component provided by the Cupertino component library, implementing the left-right sliding transition commonly seen in iOS. But how do we create custom route transition animations? The answer is PageRouteBuilder
. Below, we will explore how to use PageRouteBuilder
to customize route transition animations. For example, if we want to implement a fade-in, fade-out transition, we can do so with the following code:
Navigator.push( context, PageRouteBuilder( transitionDuration: Duration(milliseconds: 500), // 500ms transition duration pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) { return FadeTransition( // Use fade-in, fade-out transition opacity: animation, child: PageB(), // Route B ); }, ), );
As you can see, the pageBuilder
function receives an animation
parameter provided by Flutter's route manager. During the route transition, pageBuilder
is called on every animation frame, allowing us to define custom transition animations using the animation
object.
Both MaterialPageRoute
, CupertinoPageRoute
, and PageRouteBuilder
inherit from the PageRoute
class. In fact, PageRouteBuilder
is just a wrapper around PageRoute
. We can directly inherit from the PageRoute
class to create custom routes. The example above can also be implemented by defining a custom route class like this:
Defining a Custom FadeRoute Class
class FadeRoute extends PageRoute { FadeRoute({ required this.builder, this.transitionDuration = const Duration(milliseconds: 300), this.opaque = true, this.barrierDismissible = false, this.barrierColor, this.barrierLabel, this.maintainState = true, }); final WidgetBuilder builder; @override final Duration transitionDuration; @override final bool opaque; @override final bool barrierDismissible; @override final Color barrierColor; @override final String barrierLabel; @override final bool maintainState; @override Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => builder(context); @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { return FadeTransition( opacity: animation, child: builder(context), ); } }
Using FadeRoute
Navigator.push(context, FadeRoute(builder: (context) { return PageB(); }));
While both approaches can implement custom route animations, it is recommended to use PageRouteBuilder
when possible, as it avoids the need to define a new route class, making it easier to use. However, there are situations where PageRouteBuilder
may not be sufficient. For example, when applying transition animations, you may need to access certain properties of the current route. In such cases, inheriting from PageRoute
is the only option.
For instance, if we want to apply the animation only when opening a new route, but not when returning, we would need to check the isActive
property of the current route when building the transition animation. The code would look like this:
@override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { // The current route is active, meaning a new route is being opened if (isActive) { return FadeTransition( opacity: animation, child: builder(context), ); } else { // Returning, so no transition animation is applied return Padding(padding: EdgeInsets.zero); } }
For more details on route parameters, you can refer to the API documentation. The information is fairly straightforward, so we won't go into further details here.