Recommendations on how to deal with Navigation for multi-package apps with go_router?

I have a Flutter app project that splits the features into packages, each containing all the code for the screen widget, state management, business rules, api requests for a given feature, as well as should contain definition of routes to access the screens defined inside this package. The package might contain multiple pages, so there might be the need to perform navigation to screens inside the package, but also other packages might need to navigate to my package’s screens.

Is there a recommended way to deal with this using go_router package without the need for packages to directly depend on each other and also keeping type-safe routes (avoid named routes that could create some implicit dependency)?

Basically I’m trying to find some extension from what is described in this guide from VGV.

#go-router #navigation #multi-package

3 Likes

Curious, what is the main reason to split the app into multiple feature packages?

We have a big project (monorepo), with around 10+ teams working in it, with potential to grow even more in the upcoming months. By splitting features into packages, we enable, for instance:

  • more accurate definition of code ownership
  • running only affected code tests on pull requests
  • stronger control of code access for features (we can define which files are exported outside the package)
  • dependency between features and internal “core” libraries explicit (via pubspec.yaml)
4 Likes

GoRouter requires all of your routes to be defined in a single GoRouter configuration object, but you can split up those routes into separate libraries, like this:

You may need to make sure that your routes don’t conflict with each other. You may want to create a parent route for each feature (‘/feature1’, ‘/feature2/’) and define the child routes underneath that (‘/feature1/screenA’).

Hope this helps!

2 Likes

Thanks for the suggestion! It definitely works but navigating between LibraryA and LibraryB would be done by named routes, which creates some implicit dependency between the two. For a larger project, having higher control over that and even on avoiding conflict names is a must. I’ll probably work on some strategy in the upcoming month and I can share some results here that should solve these.

1 Like

Thanks, yeah unfortunately GoRouter doesn’t support navigating to a relative route (you can’t call context.go(‘newlocation’) or go(‘…/newlocation’), for example).

Actually, my previous comment is wrong now, the PR for relative routes just landed and has been published in the latest version.

1 Like

The way I’ve implemented this in our app is:

  • top level app package, with material app and GoRouter
  • intermediate modules/packages
  • base package which all modules depend on

AppRouter interface/abstract class
Defined in the base package. Contains app route definitions and/or methods to do the navigation such as toCustomers(),

This interface is provided via Riverpod or any other DI solution. All packages read and reference the AppRouter interface for navigation

AppGoRouter implementation class
Defined in the top level app module. Implements the interface and responsible for creating GoRouter with the correct routes.

This approach works pretty well for us and allows the modules to navigate without needing to reference GoRouter directly.

1 Like