Skip to main content

Get started in 5 lines

This quickstart guide shows you how to set up NavigationUtils with a minimal working example. You’ll have Navigator 2 working in your app in under 5 minutes.
1

Install NavigationUtils

Add NavigationUtils to your pubspec.yaml:
pubspec.yaml
dependencies:
  navigation_utils: ^0.9.11
Then run:
flutter pub get
2

Import the package

Import NavigationUtils in your main file:
main.dart
import 'package:navigation_utils/navigation_utils.dart';
3

Define your routes

Create a list of NavigationData to define your app’s routes:
main.dart
List<NavigationData> routes = [
  NavigationData(
    label: MyHomePage.name,
    url: '/',
    builder: (context, routeData, globalData) => const MyHomePage(),
  ),
  NavigationData(
    label: ProjectPage.name,
    url: '/project/:id',
    builder: (context, routeData, globalData) => ProjectPage(
      id: int.tryParse(routeData.pathParameters['id'] ?? '') ?? 0,
    ),
  ),
];
Each route needs a url because NavigationData maps URLs to pages. The label is optional and enables named routing like Navigator 1.
4

Initialize NavigationManager

Initialize NavigationManager before running your app:
main.dart
void main() {
  NavigationManager.init(
    mainRouterDelegate: DefaultRouterDelegate(navigationDataRoutes: routes),
    routeInformationParser: DefaultRouteInformationParser(),
  );
  runApp(const MyApp());
}
DefaultRouterDelegate and DefaultRouteInformationParser are convenience classes that handle the Navigator 2 boilerplate for you.
5

Configure MaterialApp.router

Use MaterialApp.router with NavigationManager’s delegates:
main.dart
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Navigation Utils Demo',
      routerDelegate: NavigationManager.instance.routerDelegate,
      routeInformationParser: NavigationManager.instance.routeInformationParser,
    );
  }
}
Navigator 2 uses MaterialApp.router instead of MaterialApp. The routerDelegate and routeInformationParser replace the routes and onGenerateRoute from Navigator 1.
With NavigationUtils set up, you can navigate using familiar Navigator 1 methods:
// Navigate using URL paths
NavigationManager.instance.push('/project/1');

Complete example

Here’s the complete minimal example from example/lib/main.dart:
main.dart
import 'package:flutter/material.dart';
import 'package:navigation_utils/navigation_utils.dart';

List<NavigationData> routes = [
  NavigationData(
    label: MyHomePage.name,
    url: '/',
    builder: (context, routeData, globalData) => const MyHomePage(),
  ),
  NavigationData(
    label: ProjectPage.name,
    url: '/project/:id',
    builder: (context, routeData, globalData) => ProjectPage(
      id: int.tryParse(routeData.pathParameters['id'] ?? '') ?? 0,
    ),
  ),
];

void main() {
  NavigationManager.init(
    mainRouterDelegate: DefaultRouterDelegate(navigationDataRoutes: routes),
    routeInformationParser: DefaultRouteInformationParser(),
  );
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Navigation Utils Demo',
      routerDelegate: NavigationManager.instance.routerDelegate,
      routeInformationParser: NavigationManager.instance.routeInformationParser,
    );
  }
}

class MyHomePage extends StatelessWidget {
  static const String name = 'home';
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => NavigationManager.instance.push('/project/1'),
              child: const Text('Open Project (Path)'),
            ),
            ElevatedButton(
              onPressed: () => NavigationManager.instance.push(
                ProjectPage.name,
                pathParameters: {'id': '2'},
              ),
              child: const Text('Open Project (Named)'),
            ),
          ],
        ),
      ),
    );
  }
}

class ProjectPage extends StatelessWidget {
  static const String name = 'project';
  final int id;

  const ProjectPage({super.key, required this.id});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Project $id')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Project Page $id'),
            ElevatedButton(
              onPressed: () => NavigationManager.instance.pop(),
              child: const Text('Back'),
            ),
          ],
        ),
      ),
    );
  }
}

Enable hot reload for routes

When developing, you’ll want hot reload to pick up route changes. Add this to your root widget:
main.dart
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void reassemble() {
    NavigationManager.instance.routerDelegate.navigationDataRoutes = routes;
    super.reassemble();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Navigation Utils Demo',
      routerDelegate: NavigationManager.instance.routerDelegate,
      routeInformationParser: NavigationManager.instance.routeInformationParser,
    );
  }
}
Use a getter for routes and avoid final so new instances can be created on hot reload.

Next steps

Now that you have NavigationUtils working, explore these features:

Query parameters

Pass data between pages using URL query parameters

Path parameters

Capture dynamic URL segments like /user/:userId

Navigation methods

Learn about pushReplacement(), popUntil(), set(), and more

Deeplinks

Handle deeplinks with authentication and custom logic

Build docs developers (and LLMs) love