Darc
Darc

Reputation: 883

NavigationRail with go_router

I'm developing a web/desktop application that has a fairly standard UI layout involving a NavigationRail on the left and a content pane taking up the remainder of the screen.

enter image description here

I've reciently added go_router so I can properly support URLs in web browsers, however in doing so I have lost the ability to have any form of transition/animation when moving between pages as calling context.go() causes a hard cut to the next page.

Theres also the issue that go_router routes have to return the full page to be rendered meaning I need to include the navigation rail on every page rather than each page being just the content relevant to that page. I believe this is also the main reason all animations are broken, because clicking a link effectively destroys the current navigation rail and builds a new one for the new page

I couldn't see anything in go_router but is there any form of builder API available that can output and refresh a single section of the page? I'm thinking of something like bloc's BlocBuilder which listens for state changes and rebuilds just the widget its responsible for when a change occures.

Alternatively, is there a way to update the current URL without rebuilding the whole page?

Or is go_router just not capable of what I'm after, and if so, are there any alternatives that can do this?

The overall effecti I'm after is similar to the material site https://m3.material.io/develop Clicking around the various buttons feels like you are navigating around within an app rather than clicking on links and loading new pages

Thanks for your help

Upvotes: 8

Views: 2913

Answers (1)

BeniaminoBaggins
BeniaminoBaggins

Reputation: 12503

I solved this with a Shell Route. I got the idea when going through the go_router 5 migration guide. I saw that they had a navigatorBuilder property in which they were wrapping every route in a Scaffold with AppBar. And it said to migrate:

Before migration:

final GoRouter router = GoRouter(
 routes: <GoRoute> [
   GoRoute(
     path: '/',
     builder: (_, __) => const Text('/'),
   ),
   GoRoute(
     path: '/a',
     builder: (_, __) => const Text('/a'),
   )
 ],
 navigatorBuilder: (BuildContext context, GoRouterState state, Widget child) {
   return Scaffold(
     appBar: AppBar(title: Text(state.location)),
     body: child,
   );
 }
);

After Migration:

final GoRouter router = GoRouter(
 routes: <GoRoute> [
   ShellRoute(
     builder: (_, GoRouterState state, child) {
       return Scaffold(
         body: child, 
         appBar: AppBar(title: Text(state.location)),
       );
     },
     routes: [
       GoRoute(
         path: 'a',
         builder: (_, __) => const Text('a'),
       ),
       GoRoute(
         path: 'b',
         builder: (_, __) => const Text('b'),
       )
     ],
   ),
 ],
);

Now every route in the ShellRoute.routes property will be wrapped with that Scaffold and AppBar, it will not rebuild upon navigation, so will behave as a persistent AppBar, NavigationBar, or whatever should. Now it shows the page transition animations when you do context.go('some_route') (for example from a NavigationBar in the ShellRoute). You also don't run into the issue Felix ZY was having, accessing GoRouter from the context.

A NavigationRail would just be like this in the shell route:

ShellRoute(
 builder: (_, GoRouterState state, child) {
   return Scaffold(
  body: Column(children:[Row(
    children: <Widget>[
      NavigationRail(....)]),
  child,]));
});

}

Upvotes: 2

Related Questions