Open mikuhl-dev opened 1 year ago
I've given it a go, technically made it work but with a catch.
The only technical issue preventing us from having multiple definition files is that go_router_builder
generates a list always called $appRoutes
.
So, I went ahead and instead of it always being $appRoutes
, I've set the variable name to be based on the file name the generation is starting from.
Therefore, given I have the following structure:
auth.dart
part of auth.g.dart
containing the /home
route.unauth.dart
part of unauth.g.dart
containing /login
and /signup
routes.I can easily define the router as follows:
final GoRouter _router = GoRouter(
routes: <GoRoute>[...$authRoutes, ...$unauthRoutes],
);
The drawback I see here, is that it being file based you could potentially end up with long and weird names. Such as:
Happy to discuss this further and open a PR with these changes if you want to take a deeper look into it.
I'm actually trying for the first time to use go_route_builder
and my first attempt was to attach the routes to the screens since this made most sense to me. I was surprised that I had to place all my route classes in the same file as the router.
This is one of the obious things that make me pull out hair. Forcing to put all routes in single file. Don't know who thought it was great idea.
Look at https://pub.dev/packages/katana_router how single AOP based can be...
I've given it a go, technically made it work but with a catch.
The only technical issue preventing us from having multiple definition files is that
go_router_builder
generates a list always called$appRoutes
.So, I went ahead and instead of it always being
$appRoutes
, I've set the variable name to be based on the file name the generation is starting from.Therefore, given I have the following structure:
auth.dart
part ofauth.g.dart
containing the/home
route.unauth.dart
part ofunauth.g.dart
containing/login
and/signup
routes.I can easily define the router as follows:
final GoRouter _router = GoRouter( routes: <GoRoute>[...$authRoutes, ...$unauthRoutes], );
The drawback I see here, is that it being file based you could potentially end up with long and weird names. Such as:
- auth.dart -> $authRoutes
- all_types.dart -> $all_typesRoutes
- auth_routes.dart -> $auth_routesRoutes
- PascalCaseRoutes.dart -> $pascalCaseRoutesRoutes
Happy to discuss this further and open a PR with these changes if you want to take a deeper look into it.
@dancamdev Where did you set it so it generate the variable based on filename? Did you manually change the variable names? does it gets overwritten whenever you re-run the build_runner?
Hi! I don't know if it's the best way, but that's how I've been using it:
routes.dart
import 'package:go_router/go_router.dart';
import 'package:my_project/modules/login/login.dart';
import 'package:my_project/routes/wrapped_routes/wrapped_routes.dart'
as wrapped_routes;
import 'package:my_project/modules/login/login.dart' as login_routes;
import 'package:my_project/modules/register/register.dart' as register_routes;
class Routes {
static final router = GoRouter(
routes: [
...login_routes.$appRoutes,
...register_routes.$appRoutes,
...wrapped_routes.$appRoutes,
],
initialLocation: login_routes.LoginRoute().location,
routerNeglect: true,
);
}
The trick here is to import the route file "as something" to use the $appRoutes specifically from that .g file.
For common routes (without any Shell)
login_route.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:my_project/modules/login/login.dart';
part 'login_route.g.dart';
const loginRoute = '/login';
@TypedGoRoute<LoginRoute>(path: loginRoute)
class LoginRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const LoginPage();
}
}
For a Shell route type, that's my "wrapper" (I named like that, but basically it's a ShellRoute definition):
wrapped_routes.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:my_project/modules/home/route/home_route.dart';
import 'package:my_project/modules/wrapper/wrapper.dart';
part 'wrapped_routes.g.dart';
@TypedShellRoute<WrapperRouteData>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<HomeRouteData>(path: homeRoute),
],
)
class WrapperRouteData extends ShellRouteData {
const WrapperRouteData();
@override
Widget builder(
BuildContext context,
GoRouterState state,
Widget navigator,
) {
return WrapperPage(child: navigator);
}
}
And then we can define only the GoRouteData for that route (since the Typed itself goes under the wrapped_routes.dart
file:
home_route.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:my_project/modules/home/home.dart';
class HomeRouteData extends GoRouteData {
const HomeRouteData();
@override
Widget build(BuildContext context, GoRouterState state) {
return const HomePage();
}
}
const homeRoute = '/home';
I have a feeling that can still be improved, but please let me know if there's a better way.
PS: I've separated my project under modules, but that's totally optional.
Recently I was building an app with two different sign in flows one for when you have a persisted account and one for normal sign in. I thought of leveraging the routes for the two different flows but I realised that I can't use the same route as child route for multiple routes because of code generation. So my solution is same as @HAlbertin one but it seems super counterintuitive as we have to be super careful which route we import and in cases where there is a name conflict also have to use import '...' as x
which bloats the code.
Persisted Signin routes code:
part 'persisted_signin_routes.g.dart';
@TypedGoRoute<PersistedSignInFlowRoute>(
path: '/persistedsignin',
routes: [
TypedGoRoute<SignInRoute>(path: 'signin'),
TypedGoRoute<SignUpRoute>(path: 'signup'),
],
)
class PersistedSignInFlowRoute extends GoRouteData {
const PersistedSignInFlowRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) =>
const PersistedSignInScreen();
}
class SignInRoute extends GoRouteData {
const SignInRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) =>
const SignInScreen(
isPersisted: true,
);
}
class SignUpRoute extends GoRouteData {
const SignUpRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) =>
const SignUpScreen();
}
Normal Signin Routes code:
part 'signin_routes.g.dart';
@TypedGoRoute<SignInFlowRoute>(
path: '/signin',
routes: [
TypedGoRoute<SignUpRoute>(path: 'signup'),
],
)
class SignInFlowRoute extends GoRouteData {
const SignInFlowRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) =>
const SignInScreen();
}
class SignUpRoute extends GoRouteData {
const SignUpRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) =>
const SignUpScreen();
}
So here I got SignUpRoute
in both files. Maybe I overlooked this issue and in fact you are able to use a route as child of multiple diffrent routes. If not this seems as a big deal.
If this is the only workaround it seems more confusing to use Type-safe routes than normal ones.
Are there any updates on when this will be working?
Just came across this in my new project and i was also surprised that there is no better solution.
Would love to see a more dynamic approach using e.g. macros.
Use case
It looks like with go_router_builder, you are supposed to define all your routes into this one gigantic file, this also leads to making two different classes that essentially represent the same thing, like a HomeRoute, and HomePage that you pass in the HomeRoutes build method. At this point the code generation only solves the compile time checking, and instead leaves you with even MORE boilerplate.
Proposal
We should be able to define route data in separate files.