csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
442 stars 96 forks source link

Empty path GoRouter container #63

Closed oravecz closed 2 years ago

oravecz commented 2 years ago

I do all of my auth routing (and services/ux) in a package imported into my application. So, I declare my routes for authentication in my package, and my routes for my app in the app.

In my application, I use the router like this:

final appRouter = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      redirect: (_) => '/home',
    ),
    GoRoute(
      name: 'home',
      path: '/home',
      pageBuilder: (context, state) => MaterialPage(...),
    ),
    ...authRoutes,    // exported from my auth package
  ],
  errorPageBuilder: (context, state) => ...,
  redirect: _guard,     // redirects to /signin
  debugLogDiagnostics: true,
);

And in my auth package, I export the authRoutes

final authRoutes = [
  GoRoute(
    path: '/signin',
    pageBuilder: (context, state) {...},
  ),
  GoRoute(
    path: '/signup',
    pageBuilder: (context, state) {...},
  ),
  GoRoute(
    path: '/signout',
    pageBuilder: (context, state) {...},
  ),
  ...
);

I want all my auth routes to be top level routes, however if I want them to have their own guard or react to an observable login provider, I cannot do that unless I wrap them in a GoRouter(...) class where I can provide those properties. However, if I do that, I have to give that outer GoRouter() class a path. Since I don't want my existing authentication paths to be under a path like 'auth/' is there a way for me to accomplish this?

csells commented 2 years ago

there isn't a way to nest or combine multiple GoRouter objects into a single one with combined behavior today. how would that work?

oravecz commented 2 years ago

Here is what I am doing today -- currently go_router@1.1.3

My app is using my_auth package where I have encapsulated all of my authentication logic and ux into its own package.

The app's router is augmented with routes it pulls from the my_auth package using the function getAuthRoutes(...).

final appRouter = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      redirect: (_) => '/home',
    ),
    GoRoute(
      path: '/home',
      pageBuilder: (context, state) => MaterialPage(
          key: state.pageKey,
          child: const HomePage(),
       ),
    ),
    getAuthRoutes(wrapper: authWrapper),
  ],
  errorPageBuilder: (context, state) => MaterialPage<void>(
    key: state.pageKey,
    child: ErrorPage(error: state.error),
  ),
  redirect: _guard,  // if not logged in, redirect to /auth/signin (with 2.0, it will be redirected to 'auth_signin')
)

The my_auth package, exports the getAuthRoutes(...) function.

GoRoute getAuthRoutes({AuthWrapper? wrapper}) {
  final wrappedBuilder = _pageBuilder(wrapper: wrapper);

  return GoRoute(
      name: 'auth',
      path: '/auth',
      pageBuilder: wrappedBuilder(child: const SizedBox.shrink()),
      observers: [...],
      redirect: (_) => '/auth/signin',
      routes: [
        GoRoute(
          name: 'auth_signin',
          path: 'signin',
          pageBuilder: wrappedBuilder(child: const SigninPage()),
        ),
        GoRoute(
          name: 'auth_signup',
          path: 'signup',
          pageBuilder: wrappedBuilder(child: const SignupPage()),
        ),
        GoRoute(
          name: 'auth_signout',
          path: 'signout',
          pageBuilder: wrappedBuilder(child: const SignoutPage()),
        ),
        ...
      ]);
}

Because I want my auth package may need its own observers (or custom error handler, or some other property of the GoRouter class, I wrap my individual routes in a dummy GoRouter instance. This gives me control over those properties which I want to be global to my package, not my app.

However, the current API constricts me a bit when I do this. I never intend for anyone to navigate to the dummy GoRouter at '/auth'. It is simply there as a placeholder. But it requires a path and it requires a pageBuilder.

Perhaps my use case is supported if pageBuilder and path were nullable, or perhaps a GoRouter could extend a GoRouterContainer class without those properties?.