GoRouter.dev

Historical source · 2021–2022

Original guide · Navigator Integration

Preserved original documentation, with its original version context.

Edition published 21 Oct 2026Original APIs
On this page
Recovered from an earlier source commit dated 2021-12-01. This guide was later removed from the main snapshot.
Historical text from the original project repository (2021–2022). For the current APIs, use the 2026 field guide. Original wording and credits are preserved.

If you'd like to integrate with the built in Navigator widget, you can, as shown in this snippet from the navigator integration example:

dart / example
Future<void> _addPerson(BuildContext context) async {
  final person = await Navigator.push<Person>(
    context,
    MaterialPageRoute(
      builder: (context) => NewPersonScreen(family: widget.family),
    ),
  );

  if (person != null) {
    setState(() => widget.family.people.add(person));
    context.goNamed('person', params: {
      'fid': widget.family.id,
      'pid': person.id,
    });
  }
}

This example shows how to use the Navigator widget to push a new page and gather the result. It doesn't use the GoRouter but works just the way you'd expect, e.g. it will create a new page on the stack of pages, show a Back button in the AppBar, etc. To return results, use Navigator.pop as you normally would:

dart / example
ButtonBar(children: [
  TextButton(
    onPressed: () => Navigator.pop(context),
    child: const Text('Cancel'),
  ),
  ElevatedButton(
    onPressed: () {
      if (_formKey.currentState!.validate()) {
        final person = Person(
          id: 'p${widget.family.people.length + 1}',
          name: _nameController.text,
          age: int.parse(_ageController.text),
        );

        Navigator.pop(context, person);
      }
    },
    child: const Text('Create'),
  ),
]),

You can feel free to use GoRouter and Navigator together like this. However, one thing that using Navigator.push in this way will not do is update the browser's address bar. If you want that, you'll need to use GoRouter.go or GoRouter.push. You'll notice, however, that neither go nor push provides for waiting on results. That's because GoRouter is meant for use with deep and dynamic linking, i.e. everything about a location in your app can be expressed as a link. If you're calling Navigator.push and waiting on the results from some random place in your app, that's not something that can be represented in a link.

However, that doesn't mean that you can't use GoRouter to navigate to a page that gathers user input; in fact, that's a common scenario that GoRouter supports. Because the results of the navigation are not available to the caller when using the GoRouter, the screen itself would be responsible for managing what happens to the user input, e.g.

dart / example
ButtonBar(children: [
  TextButton(
    onPressed: () =>
      // Navigator.pop(context) would work here, too
      context.goNamed('family', params: {'fid': widget.family.id}),
    child: const Text('Cancel'),
  ),
  ElevatedButton(
    onPressed: () {
      if (_formKey.currentState!.validate()) {
        final person = Person(
          id: 'p${widget.family.people.length + 1}',
          name: _nameController.text,
          age: int.parse(_ageController.text),
        );

        widget.family.people.add(person);

        context.goNamed('person', params: {
          'fid': widget.family.id,
          'pid': person.id,
        });
      }
    },
    child: const Text('Create'),
  ),
]),

With this implementation, you can set up a sub-route to provide a page to add a new person:

dart / example
GoRoute(
  name: 'home',
  path: '/',
  builder: ...,
  routes: [
    GoRoute(
      name: 'family',
      path: 'family/:fid',
      builder: ...,
      routes: [
        GoRoute(
          name: 'person',
          path: 'person/:pid',
          builder: ...,
        ),
        GoRoute(
          name: 'new-person',
          path: 'new-person',
          builder: (context, state) {
            final family = Families.family(state.params['fid']!);
            return NewPersonScreen(family: family);
          },
        ),
      ],
    ),
  ],
),

And now your _addPerson method would look like this:

dart / example
void _addPerson(BuildContext context) {
  context.goNamed('new-person', params: {'fid': widget.family.id});
}

And finally, when you're making changes to the data in your app that appears in more than one place, e.g. updating the list of Person objects in a Family on one page while also showing a list of those Person objects on another page, you'll want to ensure that the widgets in your app can be rebuilt when the state they depend on changes. For this, I recommend a state management solution like provider.

There's no reason not to use the Navigator in your app when you're using the GoRouter, but you'll want to be careful how you manage your app's state and the implications of using Navigator vs. GoRouter for navigation.

Sources & version context

GoRouter.dev documentation edition. The preserved text describes the original package APIs. Follow the current guide before using it in a new project.

Find your next route