Introduction
In Flutter, smooth navigation is vital for user experience. While Navigator 1.0 works well for basic use cases, complex flows—deep linking, browser-like back/forward, URL synchronization—demand Navigator 2.0. This tutorial introduces new devs to Navigator 2.0, covering core concepts and a code-forward setup, so you can handle modern app routes with confidence.
Understanding Navigator 2.0 Concepts
Navigator 2.0 revolves around three pillars:
Pages: Immutable objects describing a route (e.g., MaterialPage).
RouterDelegate: Controls the stack of pages. It exposes methods like setNewRoutePath and manages a List<Page>.
RouteInformationParser: Converts between a URL (or OS route string) and a typed configuration object.
Key benefits of this navigation approach:
Declarative route stacks.
URL-based routing for web and deep links.
Fine-grained control over back button behavior.
Setting Up a Basic Navigator 2.0
Below is a minimal example illustrating how to wire up Navigator 2.0. It renders two pages: Home and Details.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final _routerDelegate = MyRouterDelegate();
final _routeParser = MyRouteInformationParser();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: _routerDelegate,
routeInformationParser: _routeParser,
);
}
}
class MyRoutePath {
final bool isDetails;
MyRoutePath.home() : isDetails = false;
MyRoutePath.details() : isDetails = true;
}
class MyRouteInformationParser extends RouteInformationParser<MyRoutePath> {
@override
Future<MyRoutePath> parseRouteInformation(
RouteInformation info) async {
final uri = Uri.parse(info.location ?? '');
if (uri.pathSegments.contains('details')) {
return MyRoutePath.details();
}
return MyRoutePath.home();
}
@override
RouteInformation restoreRouteInformation(MyRoutePath path) {
return RouteInformation(
location: path.isDetails ? '/details' : '/',
);
}
}
class MyRouterDelegate extends RouterDelegate<MyRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<MyRoutePath> {
final GlobalKey<NavigatorState> navigatorKey;
bool showDetails = false;
MyRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
@override
MyRoutePath get currentConfiguration {
return showDetails ? MyRoutePath.details() : MyRoutePath.home();
}
void _handleTap() {
showDetails = true;
notifyListeners();
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
MaterialPage(key: ValueKey('HomePage'), child: HomeScreen(onTap: _handleTap)),
if (showDetails)
MaterialPage(key: ValueKey('DetailsPage'), child: DetailsScreen()),
],
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
showDetails = false;
notifyListeners();
return true;
},
);
}
@override
Future<void> setNewRoutePath(MyRoutePath path) async {
showDetails = path.isDetails;
}
}
class HomeScreen extends StatelessWidget {
final VoidCallback onTap;
HomeScreen({required this.onTap});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: onTap,
child: Text('Go to Details'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details')),
body: Center(child: Text('Detail View')),
);
}
}This example demonstrates:
Defining a custom RouteInformationParser.
Implementing RouterDelegate to manage navigation state.
Building a declarative page stack.
Updating the Navigation Stack
In Navigator 2.0, you control navigation by mutating the list of pages. For dynamic flows, you can:
Add pages (pages.add(...)) to push.
Remove pages (pages.removeLast()) to pop.
Reorder pages for nested navigation or tabs.
Example: toggling a login screen before Home:
bool loggedIn = false;
List<Page> buildPages() {
final pages = <Page>[];
if (!loggedIn) {
pages.add(MaterialPage(child: LoginScreen(onLogin: () {
loggedIn = true;
notifyListeners();
})));
}
if (loggedIn) {
pages.add(MaterialPage(child: HomeScreen(onTap: showDetails)));
if (showDetails) {
pages.add(MaterialPage(child
This pattern scales to complex requirements—nested navigators, guarded routes, deep linking—by updating the page list in your build method.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
By mastering Navigator 2.0, you gain full control over routing, deep linking, and back-button behavior in Flutter. Start small with a custom RouterDelegate and RouteInformationParser, then expand to nested navigation, authentication flows, or web-style URLs. Now you’re ready to build scalable Flutter apps with robust navigation and seamless routing. Happy coding!