Using Flutter’s Navigator 2.0 with Voyager Router

Łukasz Wiśniewski
Flutter Community
Published in
5 min readJan 5, 2021

Learn how to use Voyager library and Navigator 2.0 API together. Benefit from Flutter’s new features like browser history and declarative navigation without the need for all the boilerplate code.

The Problem: Navigator 2.0 Complexity

The new Navigator API is a fairly recent addition to Flutter. If you haven’t heard about it before I highly recommend checking this article by John Ryan: Learning Flutter’s new navigation and routing system.

Navigator 2.0 API overview

Needless to state the above looks complex. There is even an issue to make it simpler. You might quickly get overwhelmed by all the boilerplate code you need to put in first. While the new API is definitely powerful, getting everything right is a challenge.

If you just use Navigator and pages, the API is quite straightforward and declarative. Just state your pages and behold.

Unfortunately, as soon as you try using MaterialApp.router things get more complicated. You need to create RouterDelegate and RouteInformationParser. Your pages get encapsulated inside what one would call a controller. This is no longer declarative approach. You end up with an API similar to Navigator 1.0.

The Solution: Keep It Declarative, Simple.

Voyager builds on top of this new API and tries to make developer experience nicer.

Here’s how we could use Voyager with the plain Navigator.

Navigator.pages + VoyagerStack

As a precondition, we need to have Voyager’s router loaded (more info here). Once that is done, you can use the router instance with a VoyagerStack. The stack’s method asPages returns List<Page<dynamic>> instance that the Navigator is expecting.

If you plan to use the stack with the MaterialApp, you don’t need to worry about instigating the delegate and the information parser — just use VoyagerStackApp instead:

MaterialApp — Declarative Way

Put your MaterialApp within a createApp builder and handle system events from callbacks as you see fit. No need to worry about the delegate and your navigation stays declarative.

Nonetheless, if you prefer the imperative approach, you have an option to use VoyagerInformationParser and VoyagerDelegate directly:

MaterialApp — Imperative Way

Page Arguments and Scopes

The VoyagerStack can contain not only pages but also other stacks. Pages can take arguments, stacks can take scopes.

Example onboarding flow

In the above snippet, we’re providing a Bloc instance across multiple pages at once. This can be helpful when building complex flows that need to share common state.

Stack Serialization

You might not need stack serialization. This is only needed if you want to support browser history and have custom types in your stack (e.g. Bloc used as a scope). Since Flutter has no reflection, we need to register non-standard types manually — in our case it’s the LoginBloc:

Registration of non-standard types

Under the hood the VoyagerInformationParser will use registered adapters to persist the stack.

Stack Manipulation

The stack object is immutable. If you wish to modify the stack contents, you need to use mutate method:

This method gives you a copy of the underlying array which you can modify. For convenience, the stack comes with predefined removeLast method:

Unlike List.removeLast, this method removes nested stacks recursively. Additionally the scope element can implement VoyagerScopeRemovable — using VoyagerStack.removeLast can trigger resources disposal automatically.

Page Animations

By default all stack pages are wrapped with a MaterialPage when you call asPages method. If needed, you can provide your own wrapper using defaultPageBuilder parameter:

For finer control, you can register PagePlugin. This will make page wrapping a part of your navigation schema, customizable per each destination. Say we wanted to animate login screen from the bottom, this is what it could look like:

Voyager’s Navigation Map

Learn More

Check all the implementation details in the project’s example app. If you wish to try out Voyager with latest Navigator 2.0 API, you’ll need a 3.x version (a nullsafety prerelease):

Want to more about Voyager? Check the talk I gave during Droidcon SF 2019 — it’s a bit outdated now but it still contains relevant information about the core architecture.

Finally, don’t forget to check the project on GitHub — leave a ⭐, create an issue if you find a problem or open a PR. Thank you for your reading time 💖

🔗 Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm

--

--