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.
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)
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’).
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.
Thanks, yeah unfortunately GoRouter doesn’t support navigating to a relative route (you can’t call context.go(‘newlocation’) or go(‘…/newlocation’), for example).
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.
@johnpryan I don’t think it’s really something that needs to be turned into a package in order for others to benefit from, this setup is really just abstracting implementation from interface.
Something like this:
// defined in base module, usable in intermediate modules and app module
abstract class AppRouter {
GoRouter get router;
GlobalKey<NavigatorState> get navigatorKey;
void goHome();
void goBooks();
void goBookDetail(String id);
}
// defined in app module, used by MaterialApp
class AppGoRouter implements AppRouter {
late final GoRouter _router;
AppGoRouter() {
_router = GoRouter(
// setup routes etc
);
}
@override
GoRouter get router {
return _router;
}
@override
void goHome() {
_router.go("/");
}
@override
void goBooks() {
_router.go("/books");
}
@override
void goBookDetail(String id) {
_router.go("/books/:id");
}
}
Thanks for sharing some sample code - is the main benefit to give developers type-safe access to the destinations in the app? That seems like something go_router_builder aims to solve.
The main benefit here to is to define an interface of navigable routes available to all modules in a monorepo, where the actual route and definition may be defined in any of the monorepo packages.
I don’t believe go_router_builder would provide any benefit in this case as it would build routes in the package they are defined in, which may not be accessible to another package.
That’s interesting - how do you ensure that your app isn’t navigating to a broken link, for example if a code change changes the name of a URL. Is that something the developer needs to write tests for, or is there some other way to check that in the monorepo setup you have?
Using contents of this forum for the purposes of training proprietary AI models is forbidden. Only if your AI model is free & open source, go ahead and scrape. Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.