1. “Persistent” AppBar across screens

Usually we use MaterialPageRoute / CupertinoPageRoute to navigate to a new screen, depending on the platform we targeted.

We are granted a Fade Upwards Page Transition by default. But this is not what we wanted. AppBar ain’t “persistent”!

Default Page Transition using MaterialPageRoute

We can achieve the desired “persistent” AppBar effect by customizing our own PageRoute . Rather, creating an illusion that the AppBar is “persistent”.

First, we extend PageRoute and name it FadePageRoute .

To speed up, you can keep the implementation of MaterialPageRoute<T> . The only critical methods/properties we need to pay attention to are:

buildTransition method: This method defines how the next screen shows up and leaves the screen. We will create a separate transition FadeInPageTransition to fit our needs.

This method defines how the next screen shows up and leaves the screen. We will create a separate transition to fit our needs. transitionDuration properties: You can control the duration it takes to complete the screen transitions.

In FadeInPageTransition , we take few parameters in constructor:

Animation<double> routeAnimation : Animation which will be used during route transition. It plays from 0.0 to 1.0 during Navigator.push , and 1.0 to 0.0 during NavigatorNavigastor.pop .

: Animation which will be used during route transition. It plays from 0.0 to 1.0 during , and 1.0 to 0.0 during . Widget child : In short, our next screen DetailPage() .

We then wrap our child with FadeTransition , taking an opacity animation with respect to routeAnimation to create a fade in effect.

And voilà, we created an effect as though the AppBar is “persistent” across screens.

This is also why I insisted to wrap quotation marks around the word persistent. If you can’t create genuine static AppBar , you trick their eyes instead (evil grin).

Fade In Page Transition

2. Tweening text size with Hero across screens

We will use Hero widget for shared elements transition.

It’s pretty straightforward, just wrap the widget with Hero and assigned same tag across screens. The framework will complete the remaining heavy-lifting for you. Done and dusted!

Hero mapped between two screens

Glitchy title is glitchy

Sadly, the result is kinda glitchy for Text widget. This is not okay!

In fact, this is the expected behavior with the default implementation of Hero widget.

Slightly in-depth explanation of why this could happen:

Hero computes the size of source hero widget and size of destination hero widget. Hero creates RectTween to animate the Widget from size of source to the size of destination. Hero animates the destination hero widget from source location and size to where it supposed to be in the next screen (which is why we see big text appears immediately during enter transition, and it slowly enlarges to the size of its destination).

In short, the default Hero widget is not designed to animate text size and widget size during screen transition in the first place. But it offers a way for us to achieve the desired effect, through flightShuttleBuilder .

flightShuttleBuilder defines the widget when it “fly” across screen. By default, if nothing is provided for flightShuttleBuilder , Hero automatically uses our destination hero widget to fly across the screens.

To solve this, we will need to customized our own flightShuttleBuilder , providing an animated Text widget in the Hero constructor.

We will focus on Title (highlighted in green in the previous image) for this example.

The Title widget is shared across screens, only varied with different font sizes according to the requirements (static | animating | enlarged on main screen | shrunk on detail screen). Therefore, we need to define the state to choreograph each case accordingly.

First, create an enum ViewState as a mean to choreograph the widget.

Second, we create a StatefulWidget for our Title that takes in ViewState as parameter, so it behaves accordingly with the ViewState passed in.

Third, we define how the widget should behave according to each ViewState . I defined the animation logics in initState because I wanted the widget to animate immediately when it is instantiated.

Lastly, create your flightShuttleBuilder that emits desired ViewState according to the HeroFlightDirection . Therefore, when the shared elements transition is triggered, our Text widget will fly to its destination and update its text size concurrently.

This is it. We apply the same logic to the app header and icon.

Hooray! The Title is sized properly with the transition now!

Hero transition with text size animation

P.S.: Alternatively, you can also pass in animation from flightShuttleBuilder as parameters to the animated StatefulWidget , then use animation.drive(yourTween) to synchronize the animations. I supposed this is a cleaner method to achieve the same effect. Please feel free to share your solution with me.

3. Choreographing animations across screens with Navigator

If you look at the AppBar carefully, the hamburger menu icon animates to back icon on entering into new screen, and vice versa.

We can animate the AnimatedIcon simply using a pre-defined animation, and call it before pushing new screen.

Easy peasy lemon squeezy!

Animated hamburger icon on Navigation

OH WAIT, the AnimatedIcon doesn’t animate back to hamburger when the detail screen pops! ΣΣ(ﾟДﾟ;)

AnimatedIcon doesn’t animate back to animate icon when the screen pops

How do we choreograph animation to get back to its initial state when screen pops? And if possible, can we expand the trick to other use cases?

ValueNotifier to the rescue!

We need to keep a variable that hold information across screens. The ValueNotifier wrapping that variable will then react according to the variable whenever it is updated.

First, we define a variable bool returnFromDetailPage as a trigger when we enter a new route or return from a route. Wrap the variable in ValueNotifier so that we can listen to the variable when it updates according to the navigation.

Second, when we pop the screen on top of the stack, we return a value to the ValueNotifier . The ValueNotifier will then trigger its listener to animate accordingly.

Here we go! Our menu icon AppBar animates as desired.

Animated AppBar on page transition

The completed screen transition as per design (though not 100% alike :X)

You can apply the same logic to trigger some fancy animations tailored to the data returned from other screen. Sweet!