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.