Flutter Route Animations

Nash
Flutter Community
Published in
8 min readDec 16, 2019

Picture this, you open an app, tap a button and another screen fades in…cool but boring. As a developer, you are always looking for ways to make your app stand out in the sea of millions. For those who are building their apps with Flutter, custom animations are not difficult. An AnimationController paired with a few tweens and you’re equipped to rule the world.

Under the hood, Flutter uses Route Animations to handle screen transitions. The framework has two main types out routes, PageRoute and PopupRoute. As the name would suggest, page routes are mainly used for navigating from screen to screen while popup routes are used for handling dialogs and modals.

In this article, we will have a look at the various routes and classes used by the framework. Since PageRoute is widely used and pretty straightforward, we’ll build a small sample project using its sibling, PopupRoute.

To understand PopupRoute we need to start at the very beginning. What even is a Route?

Routing is at the core of all apps. Popups, pages/screens, dialogs all use routes in one form or another. In Flutter, there are four types of routes:

  • Route
  • OverlayRoute
  • TransitionRoute
  • Modal Route
  • Popup Route

Route

Route is the base class used by all routes in Flutter. It is an abstract class which defines an interface for interaction with the Navigator. It contains properties for whether the page/screen is active and at the top of the navigator stack or inactive and at the bottom. Getters are also defined for retrieving the result when the route is "popped" by the Navigator and the current list of overlay entries used by this route (more on the overlay stuff later).

Also defined in Route are methods for handling rebuilds, state change, changes to the next and previous route in the stack and callbacks for when the route is popped.

Below shows a full list of the available methods and their functionality:

Overlay Route

Next, we move to OverlayRoute. As the name suggests, an OverlayRoute is a route that displays widgets in the Navigator overlay, above the current route. The constructor of this class takes on an optional parameter, RouteSettings which is passed to the superclass, Route.

Included in this class are two additional properties, finishedWhenPopped and overlayEntries. finishedWhenPopped returns a true when didPop successfully removes or "pops" the route and it is disposed of by the Navigator. Overlay entries, on the other hand, is a list of entries the route has placed on the Overlay.

What is an Overlay

A section talking about OverlayRoute would not be complete without mentioning Overlay and OverlayEntry. An overlay in Flutter is a Stateful widget that contains a list of the onstage and offstage children. The class Overlay takes an optional list of overlay entries, initialEntries. If no entries are specified, an empty List is automatically created. Also in this class is a static of method for retrieving the closest instance of OverlayState. This is useful since if you'd like to add an overlay to the screen, you'd need to call the insert method located in the state class.

The real magic happens in the state class. Here, two very important methods exist, insert and insertAll. When the state is first created, the initialEntries are added using the insertAll method. As you might've guessed, this method takes an Iterable of overlay entries. The elements in this Iterable check whether they should be inserted above another entry (passed to insertAll as an optional parameter) then inserted into the current list of entries at the appropriate index. The process is pretty much the same for the insert method, only this time there is no need to loop over any list.

What is an Overlay Entry

At this point, you’ve probably seen the term “overlayEntry” quite a lot. Let’s take a few minutes to briefly have a look at it since it is the foundation of the Overlay class, after all, it is the class that is being inserted and removed everywhere.

An Overlay Entry is a class which contains a WidgetBuilder and

Transition Route: Animations 🎉

What’s the one thing missing from routes at this point? Animations of course!

Transition route as the name implies, adds entrance and exit animations to routes. It creates and manages an AnimationController which is used to drive the transition. The controller, animation and duration are all exposed via getters.

Transition Route contains a Completer which is used to check whether the transition is finished or if the overlay entries have been removed from the navigator's overlay. The future also completes once the animation has been dismissed after poppedwas called. Both createAnimationController and createAnimation utilize this completer to ensure the route is not disposed of before carrying out their function.

The various animation statues are handled internally by the function _handleStatusChanged. A completed animation changes the opaque value of the first overlay entry to reflect that of the opaque value passed by the user. Should the route be opaque, the route behind it will not be built in an attempt to save resources. Both reverse and forward statuses check the overlayEntries to ensure they are not empty then set the opaque value of the first overlay entry to false. This is done since while the route is being animated, the previous one will still be visible. The final animation status dismissedchecks to make sure the route is not active before calling navigator.finalizeRoute to properly dispose and remove the route.

Modal Route

Implemented by PageRoute and PopupRoute, a modal route simply block all interaction with the previous route. That said, not all ModalRoute is opaque. Popups such as dialog boxes and menus can overlap the previous route without hiding the content that is beneath it.

Modal routes also take a <T> argument that is used as the return value of the route. Should there be no return value, it is recommended that you use void as the return value.

Like the classes before it, ModalRoute also exposes additional properties to the user. The ability to control how the route is dismissed, the label and color of the barrier, whether the route should maintain its state when inactive and the primary and secondary animations are just some of the noteworthy properties exposed.

Defined in the class ModalRoute are the methods buildTransitions and buildPage. As the names imply, build transitions is used to animate the static content of the page. This method is called every time the route state changes. Since its primary purpose is for animations, you should avoid building static content in this method, buildPage should be used instead since it would be more efficient.

Build Transitions takes the following properties:

Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double>
secondaryAnimation,
Widget child,
)

Note: The child property passed to this function is the result/return value of buildPage.

Build Page as mentioned earlier is used to build the static content of the page. The primary content of the route is built using this method. It is called when the route is first built and is rarely (if ever) called after that point. Unlike buildTransitions, it is not automatically called when the state changes unless ModalRoute.of is used.

The signature of buildPage:

Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
)

Putting it all together

We’ve covered a lot of theory in this article but the question remains, how does this all come together? How do I build something cool with this?

Here is a demo video of what we will be building:

Can you spot the transitions? In our example, we will be combining:

— Popup Routes

— Rotation Transitions

— Scale Transitions

0: Project Configuration 🛠

Let’s start by creating a new Flutter project, feel free to use any name you like. Once you’ve created a project, create an assets directory and drag an image of your choice into the folder.

Note: Don’t forget to declare your asset in pubspec.yaml. See here for detailed instructions.

1: Pages 📒

Before we get into any custom routes, let’s first create some pages for us to play with. For the purposes of this demo, I created a home page containing a ListTile with the onTap set to an empty function.

Home page code

The second page is also very basic, it uses an AnimatedPadding to add a margin around the page content. We use the window’s viewInsets along with an extra padding of 30 pixels vertical and 24 pixels horizontal. As for the body of the page, a Material is used to apply a background color and border-radius. Finally, we center our image on the page using an Image.asset.

2: Time to get Nerdy 🤓

To create our custom route, create a class extending PopupRoute. Once created, you will be prompted to override a few methods.

Here is an overview of the generated methods:

barrierColor — Controls the background color used by the modal route. If this is left null, the barrier will be transparent.

barrierDissmissible — Controls whether the route can be dismissed when the user taps the barrier.

barrierLabel — If the barrier is dismissible, this label is by semantics for accessibility.

transitionDuration — The length of time for the route animation

buildPage — This method is used to build the primary contents of the route. This method should not be used to build animations.

buildTransitions — This method is used to build the route’s animation. The parameter “child” is the output of the method buildPage.

Hopefully, this helps you understand the role of the different methods and properties. If you are following along, customize and change the values of these, the results are always surprising :).

Now that we’ve created the class, change the functions and add some variables. Let’s create a final member variable builder of type WidgetBuilder. This will be passed to the constructor of the class. Next, call this function in buildPage.

This handles building the content of the page. Time to add animations and make everything fancy ✨. Let’s move on to buildTransitions. As previously mentioned, this method handles building animations for your route. The child parameter passed to this function represents the output of buildPage. Feel free to customize and mix transitions, I am going to keep things boring and use a RotationTransition and ScaleTransition.

Great! Run the project and build the test app. If all is well, you should have a layout similar to mine.

Change the color, experiment with secondary animations, the possibilities are limitless.

Conclusion 💭

Routing and navigation are very large topics. This article focused on animations, a small piece of a much larger puzzle. For those who’d like to learn a bit more on the topic, here are a few resources you can check out:

  1. The Flutter Whisperer (Simon Lightfoot) and Mariano Zorrilla’s talk at Flutter NYC — Coming Soon

2. Clean Navigation in Flutter Using Generated Routes

3. Navigation and Routing

4. API docs

The code for this article can be found on my Github.

I am excited to see what everyone builds, tag me on twitter with your animated creations!

Nash

--

--

Nash
Flutter Community

Leading DevRel @getstream_io 🥑 · Editor and Admin @flutter-community 💙