A beginner’s guide to go_router in Flutter
Tags: flutter, devops, dart, beginners
Published: 2022-11-01 13:29:54

_> Written by Hrishikesh Pathak and originally published to [Codemagic blog](https://blog.codemagic.io/flutter-go-router-guide/?utm_source=devto&utm_medium=go-router-article-devto&utm_campaign=flutter_distribution)._

Routing is a crucial aspect of an app. Just like when you manage the application state or build the UI, you should give sufficient attention to optimizing the routing of your application. An optimized routing system helps users navigate your app and can handle the user state efficiently.

`go_Router` is a declarative and minimal routing system built on top of Flutter's Router API. `go_router` provides a convenient URL-based API to navigate between different screens.

In this article, we'll help you learn how to use `go_router` in Flutter. We will discuss how to use route parameters, navigate using named routes, handle 404 errors, and much more. To get the most out of this article, be sure to read it to the end.



Adding go_router to your Flutter project
----------------------------------------

Open a terminal in your Flutter project. Then run the following command to install the [go_router](https://pub.dev/packages/go_router) package in your Flutter project.

```
flutter pub add go_router

```

This command installs the latest version of `go_router` in your project. If you want to install a different version, just replace the version number in the `pubspec.yaml` file, and run `flutter pub get` to get your desired version.

Setting up basic routing using go_router
----------------------------------------

To integrate `go_router` in your app, change your `MaterialApp` widget to `MaterialApp.router`. This constructor accepts a `routerConfig` property.

```
MaterialApp.router(
  routerConfig: _router,
  title: "Go router",
);

```

Next, we'll add a `GoRouter` object `_router` to the `routerConfig` property. Let's configure our `GoRouter` and add the *home* and *settings* routes.

```
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => const HomePage(),
    ),
    GoRoute(
      path: "/settings",
      builder: (context, state) => const SettingsPage(),
    )
  ],
);

```

When someone visits the `/` route, `GoRouter` returns the `HomePage` widget. Similarly, if someone visits the `/settings` route, `GoRouter` returns the `SettingsPage` widget.

Let's see what `HomePage` and `SettingsPage` look like in the code.

```
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Homepage"),
      ),
      body: Center(
        child: ElevatedButton(
        ),
      ),
    );
  }
}

class SettingsPage extends StatelessWidget {
  const SettingsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        title: const Text("Settings"),
      ),
      body: const Center(
        child: Text("Settings Page"),
      ),
    );
  }
}

```

How to navigate between routes with go_router
---------------------------------------------

To navigate between routes, we can use the `GoRouter.of(context).go()` method or the more concise `context.go()` method. Let's add this method in the `ElevatedButton` in our `HomePage` widget to navigate to the `/settings` route.

```
child: ElevatedButton(
  onPressed: () => context.go("/settings"),
  child: const Text("Go to Settings page"),
),

```

Similarly, add an `ElevatedButton` in the `SettingsPage` to come back to the home `/` route.

```
child: ElevatedButton(
  onPressed: () => context.go("/"),
  child: const Text("Go to home page"),
),

```

Subroutes in go_router
----------------------

In the above section, we define our `GoRouter` object. There, you can observe that we put a leading `/` in every route. If you need deeply nested routes, then you have to type the whole route with the leading `/` every time. This also makes the routes less organized.

GoRouter provides a `routes` argument in every `GoRoute` object to group subroutes and define nested routes more easily.

If we convert our previous `GoRouter` object to a subroutes structure, then the new `GoRouter` should look like this.

```
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          path: "settings",
          builder: (context, state) => const SettingsPage(),
        )
      ],
    ),
  ],
);

```

Can you see the difference? Now, the `settings` routes live inside the `/` routes. Therefore, there's no need to specify a leading `/` in subroutes.

Let's look at another example so that we can grasp the concept completely. If you want to define the nested route `/a/b/c`, then the GoRouter object should look like this.

```
final GoRouter _newRouter = GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          path: "a",
          builder: (context, state) => const PageA(),
          routes: [
            GoRoute(
              path: "b",
              builder: (context, state) => PageB(),
              routes: [
                GoRoute(
                  path: "c",
                  builder: (context, state) => PageC(),
                )
              ],
            )
          ],
        )
      ],
    )
  ],
);

```

Adding route parameters in go_router
------------------------------------

It is very easy to add route parameters in `go_router`. To define a route parameter, add a trailing `:` with the parameter name in the `path` argument of `GoRoute`.

For example, if you want to add a `name` parameter in the settings route, the `path` argument should be `/settings:name`. You can access the route parameter with the `state.params["name"]` variable.

Let's see how to implement this in our Flutter app. The modified `GoRouter` with a route parameter in the settings route should look like this.

```
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      path: "/",
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          path: "settings/:name",
          builder: (context, state) => SettingsPage(
            name: state.params["name"]!,
          ),
        )
      ],
    ),
  ],
);

```

To receive the route parameter in the settings screen, let's modify our code and add a property called `name`.

```
class SettingsPage extends StatelessWidget {
  final String name;

  const SettingsPage({super.key, required this.name});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        title: Text(name),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.go("/"),
          child: const Text("Go to home page"),
        ),
      ),
    );
  }
}

```

Now, when we visit `/settings/codemagic`, the `codemagic` route parameter is passed to the `SettingsPage` and displays the variable on the screen. Similarly, if you visit the `/settings/hrishikesh` route, `hrishikesh` is displayed in the `SettingsPage`.

Named routes in go_router
-------------------------

Writing route paths manually is cumbersome and error prone. Therefore, `go_router` offers a named route feature to navigate around the app, and `go_router` will automatically resolve the path itself.

To generate named routes, let's change our `GoRouter` configuration with the name parameter.

```
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      name: "home",
      path: "/",
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          name: "settings",
          path: "settings/:name",
          builder: (context, state) => SettingsPage(
            name: state.params["name"]!,
          ),
        )
      ],
    ),
  ],
);

```

Passing route parameters to a named route is different from what you normally see. You have to define a `params` parameter in the `context.goNamed()` function. Here's how to set up navigation to `SettingsPage` from `HomePage` with a route parameter.

```
child: ElevatedButton(
  onPressed: () => context.goNamed(
    "settings",
     params: {"name": "codemagic"},
     ),
  child: const Text("Go to Settings page"),
),

```

Passing query parameters in go_router
-------------------------------------

You have access to `queryParams` in the `context.goNamed()` function. The best thing about `queryParams` is that you don't have to explicitly define them in your route path and can easily access them using the `state.queryParams` method. You can add miscellaneous user-related data as a query parameter.

Let's look at an example to illustrate this concept. In the `ElevatedButton` in `HomePage`, let's add some query parameters to the settings route.

```
child: ElevatedButton(
  onPressed: () => context.goNamed("settings", params: {
    "name": "codemagic"
  }, queryParams: {
    "email": "example@gmail.com",
    "age": "25",
    "place": "India"
    }),
    child: const Text("Go to Settings page"),
),

```

Now we can access these query parameters inside the `GoRouter` configurations and pass them to the page. In this example, I am just printing out the values for demonstration.

```
GoRoute(
  name: "settings",
  path: "settings/:name",
  builder: (context, state) {
    state.queryParams.forEach(
      (key, value) {
        print("$key:$value");
       },
     );
   return SettingsPage(
     name: state.params["name"]!,
   );
 },
)

```

Now, when you click the button to navigate to the `SettingsPage`, you can see the following output on the console screen.

![](https://blog.codemagic.io/uploads/2022/10/queryparams.png#center)

Handling 404 errors with go_router
----------------------------------

When a user visits a screen that is not defined in the `GoRouter` configuration, it causes an exception. But `go_router` provides a very easy way to handle these errors, and you can provide a custom page to show to the user when this type of exception occurs.

We can define an `errorBuilder` argument in the `GoRouter` object to handle these 404 errors. Let's make an `ErrorScreen` page that includes a button to navigate back to the `HomePage`.

```
class ErrorScreen extends StatelessWidget {
  const ErrorScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        title: const Text("Error Screen"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.go("/"),
          child: const Text("Go to home page"),
        ),
      ),
    );
  }
}

```

Now add this `ErrorScreen` to the `errorBuilder` argument of the `GoRouter` object.

```
final GoRouter _router = GoRouter(
  errorBuilder: (context, state) => const ErrorScreen(),
);

```

That's it. Try to launch your Flutter app in the Chrome browser and navigate to a random path. You will see that `ErrorScreen` will appear as a response.

Redirecting routes using go_router
----------------------------------

Sometimes, you want to programmatically navigate your user from one page to another based on certain logic. You can enable that feature using redirects in `go_router`. You can define a redirect for every path and a global redirect for all pages.

For example, when a new user opens your application, you can navigate them to the login screen instead of the home screen. Similarly, when a logged-in user opens your application, you can navigate them to the home page. You can implement this type of feature very easily using `go_router`.

Let's see how to implement a redirect in your app. Inside the route, define a `redirect` argument. Inside the `redirect` builder, check if the user is logged in or not. If they are not logged in, navigate them to the `/login` page. Otherwise, show them the home page.

```
final GoRouter _router = GoRouter(
  routes: [
    GoRoute(
      name: "home",
      path: "/",
      builder: (context, state) => const HomePage(),
      redirect: (context, state) {
        if (userIsNotLoggedIn){
          return "/login"
        }
        return "/"
      },
    ),
  ],
  errorBuilder: (context, state) => const ErrorScreen(),
);

```

GoRouter navigation comparison: go vs. push
-------------------------------------------

GoRouter has two methods for navigating between pages: `GoRouter.of(context).go()` and `GoRouter.of(context).push()`. On the surface, they look very similar, but they function differently. The `push()` method stacks one page on another in navigation, while the `go()` method directly navigates from one path to another without any stacking.

Here's an example to demonstrate this concept. Assume you have three pages: A, B, and C. If you navigate from A to B to C using the `push` method, then C is stacked on top of B on top of A. Take a look at this diagram to understand better.

![](https://blog.codemagic.io/go_router_1_10551958591501476036_hu69eed796f406b2984cadfb8954afe09d_0_1280x1800_fit_linear_3.png)

On the other hand, if you navigate from A to B to C using the `go` method, then C is only stacked on top of A and only if A is the home page with the route `/`. B is eliminated from the navigation stack altogether. Therefore, the `go()` method leaves no routing history.

![](https://blog.codemagic.io/go_router_2_4356671897497888218_hu3d348e154913586ba8b8cc6831d84e3c_0_1280x1800_fit_linear_3.png)

You can choose which strategy works best for your apps.

Bonus: Remove the # prefix from the web URL in Flutter
------------------------------------------------------

If you launch your app on the web, you can see a trailing `#` in the URL. Sometimes it looks very annoying, and you may want to remove it. You can easily achieve this with the help of a package called [url_strategy](https://pub.dev/packages/url_strategy).

First, install this package in your Flutter project.

```
flutter pub add url_strategy

```

Then import this package in your `main.dart` file, and add the `setPathUrlStrategy()` function before the `runApp()` function.

Now, run your application. You can see that the trailing `#` is removed from the URL of your Flutter app.

Choosing between go_router, Navigator 2.0, and Beamer for navigation in Flutter
-------------------------------------------------------------------------------

Flutter has several packages you can use for routing. The most well-known ones are go_router, Navigator, and Beamer. How do you choose which navigation to use for your app?

If you are primarily targeting mobile platforms (Android and iOS) and don't have time to learn a new package for routing, it's completely fine to use Navigator 1.0. It is capable of meeting all your routing needs in Android/iOS apps.

Flutter's core strength is in its multiplatform support, which spans beyond just Android and iOS. If you want to make a real multiplatform app that supports desktop and web platforms in addition to Android/iOS, then you should migrate to Navigator 2.0. Navigator 2.0 provides a very flexible and fully customizable routing solution for truly multiplatform apps.

But a huge chunk of developers don't like the verbose nature of Navigator 2.0, which usually requires you to write boilerplate code to create a simple route. If you want to avoid these issues, you can use go_router, which is declarative and a significantly simpler solution for Flutter. If you have a medium-sized to large application and want to handle dynamic routes, `go_router` can help you minimize your effort and maximize the results.

[Beamer](https://pub.dev/packages/beamer) is the new kid on the Flutter navigation stack. Like `go_router`, Beamer is also based on the [Flutter router API](https://api.flutter.dev/flutter/widgets/Router-class.html). It makes developers' lives easier by simplifying Navigator 2.0. If you are already using `go_router`, then there is no need to switch to Beamer unless you are missing some features and Beamer can satisfy your needs.

If you are a beginner Flutter dev, you can go with either `go_router` or Beamer. Both are good and focus on developer productivity by removing complexity and boilerplate code.

Build your Flutter app using Codemagic
--------------------------------------

Our app isn't completely ready for release at this point, but let's set up a CI/CD pipeline for it anyway. Ideally, you should do this right from the start of the development process to make it easier to collaborate on the app with other developers and deliver new features to testers and customers while avoiding falling into the "it works on my computer" trap.

[Codemagic](https://flutterci.com/?utm_source=devto&utm_medium=go-router-article-devto&utm_campaign=flutter_distribution) is a CI/CD platform for Flutter applications. You can trigger a new Flutter build when you push your code to your GitHub/GitLab/Bitbucket repository. Then just download the generated artifacts from the Codemagic dashboard and run them on your devices (unless you want to publish them to stores, which Codemagic can also help you with). Let's see step by step how to set up a CI/CD pipeline with Codemagic for your Flutter application.

In case you are not a Codemagic user yet, you can sign up here:

[Sign up](https://codemagic.io/signup/?utm_source=devto&utm_medium=go-router-article-devto&utm_campaign=flutter_distribution)

1.  Create a new Codemagic project and connect your repository.

![](https://blog.codemagic.io/uploads/2022/10/flutter-codemagic-demo-1.png#center)

1.  Select Flutter as the project type.

![](https://blog.codemagic.io/uploads/2022/10/flutter-codemagic-demo-2.png#center)

1.  Under Automatic build triggering, check Trigger on push.

![](https://blog.codemagic.io/flutter-codemagic-demo-3_12360258576554782698_hud67a11eaddc47b7c12a09d03e5934ff8_0_1280x1800_fit_linear_3.png)

1.  In the Build tab, select your Flutter version, build format, and custom build arguments.

![](https://blog.codemagic.io/flutter-codemagic-demo-4_16945714986341555965_huea6a11c7175a0126411e2861416487c4_0_1280x1800_fit_linear_3.png)

1.  Click on the Start new build button to initiate the first build of your application.

2.  After the build completes, you can download the artifacts and run the app on your devices.

![](https://blog.codemagic.io/codemagic-success_1743323141458607701_hub49bc8c935556c908a329cfa5abcf637_0_1280x1800_fit_linear_3.png)

Conclusion
----------

`go_router` is a very minimalistic but powerful routing package for Flutter. It removes all the complexity that [Flutter Navigator 2.0](https://blog.codemagic.io/flutter-navigator2/?utm_source=devto&utm_medium=go-router-article-devto&utm_campaign=flutter_distribution) brings to the table and provides a very developer-friendly API to work with.

In this article, we have discussed how to use `go_router` in your Flutter app as well as some of its features, like route parameters, query parameters, named routes, handling errors, and redirection. This is just a beginner's guide for getting started with `go_router`. If you want to learn more, visit the official `go_router` [API documentation](https://github.com/flutter/packages/tree/main/packages/go_router).

If you have any questions, you can ask me on [Twitter](https://twitter.com/hrishikshpathak). Have a good day.

You can find the sample app on [GitHub](https://github.com/hrishiksh/flutter-gorouter).