Open route from two different origin routes

835 views Asked by At

In my app, I have a HomePage that displays a few selected posts and a PostListPage. From both pages I open posts.

I have this route structure.

GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => HomePage(),
      routes: [
        GoRoute(
          path: "post/:id",
          builder: (context, state) => // ...
        ),
        GoRoute(
          path: "posts",
          builder: (context, state) => PostListPage()
        )
      ],
    ),
  ],
);

When I open a post one the home page with GoRouter.go, it works as intended but when I open a post from the PostListPage, the back button routes back to the HomePage, when it should route back to the PostListPage.

How can I fix this?

I know I can use GoRouter.push instead, however, with push, on web, the route is not reflected in the URL and therefore, when refreshing the browser on a PostPage, it starts the app at the HomePage.

I could use the GoRouter.optionURLReflectsImperativeAPIsoption to avoid this URL issue, but the docs say "It is strongly suggested against setting this value to true".

1

There are 1 answers

2
Peter Koltai On BEST ANSWER

If you want to have a post/:id route from the home and the post list pages as well, you can add a child route under posts:

GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => HomePage(),
      routes: [
        GoRoute(
          path: "post/:id",
          builder: (context, state) => // ...
        ),
        GoRoute(
          path: "posts",
          builder: (context, state) => PostListPage(),
          // add this part, and implement the same builder
          routes: [
            GoRoute(
              path: "post/:id",
              builder: (context, state) => // ...
            ),
          ]
        )
      ],
    ),
  ],
);

When navigating from home, use:

context.go('/post/123')

And from the post list:

context.go('/posts/post/123')

After this navigating back should word as intended.

This is a simple example that you can run in DartPad:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp.router(
        routerConfig: GoRouter(
          routes: [
            GoRoute(
              path: "/",
              builder: (context, state) => Home(),
              routes: [
                GoRoute(
                  path: "post/:id",
                  builder: (context, state) =>
                      Post(id: state.pathParameters['id']),
                ),
                GoRoute(
                    path: "posts",
                    builder: (context, state) => Posts(),
                    routes: [
                      GoRoute(
                        path: "post/:id",
                        builder: (context, state) =>
                            Post(id: state.pathParameters['id']),
                      )
                    ])
              ],
            ),
          ],
        ),
        theme: ThemeData.dark().copyWith(
          scaffoldBackgroundColor: Color.fromARGB(255, 18, 32, 47),
        ),
        debugShowCheckedModeBanner: false,
      );
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Column(children: [
        Text('Home', style: Theme.of(context).textTheme.displayMedium),
        TextButton(
          child: const Text('Posts'),
          onPressed: () => context.go('/posts'),
        ),
        TextButton(
          child: const Text('Post 123'),
          onPressed: () => context.go('/post/123'),
        ),
        TextButton(
          child: const Text('Post 456'),
          onPressed: () => context.go('/post/456'),
        )
      ]);
}

class Posts extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Column(children: [
        Text('Posts', style: Theme.of(context).textTheme.displayMedium),
        TextButton(
          child: const Text('Post 123'),
          onPressed: () => context.go('/post/123'),
        ),
        TextButton(
          child: const Text('Post 456'),
          onPressed: () => context.go('/posts/post/456'),
        ),
        TextButton(
          child: const Text('Back'),
          onPressed: () => Navigator.of(context).pop(),
        )
      ]);
}

class Post extends StatelessWidget {
  final String? id;
  const Post({required this.id});
  @override
  Widget build(BuildContext context) => Column(children: [
        Text(
          'Post $id',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
        TextButton(
          child: const Text('Back'),
          onPressed: () => Navigator.of(context).pop(),
        )
      ]);
}