Skip to main content
  1. Posts/

Using enhanced enums with go_router

<time datetime="2022-11-09 00:00:00 &#43;0000 UTC">9 November 2022</time><span class="px-2 text-primary-500">&middot;</span><span title="Reading time">4 mins</span><span class="px-2 text-primary-500">&middot;</span> <span class="mb-[2px]"> <a href="https://github.com/cgutierr-zgz/cgutierr-zgz.github.io/edit/main/content/posts/go-router-enums/index.md" class="text-lg hover:text-primary-500" rel="noopener noreferrer" target="_blank" title=""><span class="relative inline-block align-text-bottom px-1 icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M490.3 40.4C512.2 62.27 512.2 97.73 490.3 119.6L460.3 149.7L362.3 51.72L392.4 21.66C414.3-.2135 449.7-.2135 471.6 21.66L490.3 40.4zM172.4 241.7L339.7 74.34L437.7 172.3L270.3 339.6C264.2 345.8 256.7 350.4 248.4 353.2L159.6 382.8C150.1 385.6 141.5 383.4 135 376.1C128.6 370.5 126.4 361 129.2 352.4L158.8 263.6C161.6 255.3 166.2 247.8 172.4 241.7V241.7zM192 63.1C209.7 63.1 224 78.33 224 95.1C224 113.7 209.7 127.1 192 127.1H96C78.33 127.1 64 142.3 64 159.1V416C64 433.7 78.33 448 96 448H352C369.7 448 384 433.7 384 416V319.1C384 302.3 398.3 287.1 416 287.1C433.7 287.1 448 302.3 448 319.1V416C448 469 405 512 352 512H96C42.98 512 0 469 0 416V159.1C0 106.1 42.98 63.1 96 63.1H192z"/></svg> </span> Suggest an edit</a> <a href="https://github.com/cgutierr-zgz/enhanced_enums_and_go_router" class="text-lg hover:text-primary-500" rel="noopener noreferrer" target="_blank"> ยท <span class="relative inline-block align-text-bottom px-1 icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg> </span> View source </a> </span>

go_router by Flutter.dev

In this post I want to show how I like to use the new enhanced enums with go_router. This can also apply if you use any other navigator, I just enjoy using go_router.
This post was made using go_router version 5.1.5

What are enhanced enums? ๐Ÿค” #

Enhanced enums are a great way to define a set of values that are known at compile time. They are similar to the enum type, but they can also have methods and properties.

The new enhanced enums are available since Dart 2.17

How to use enhanced enums ๐ŸŽฏ #

First, ensure your dart/flutter project is using at least Dart 2.17.0 in your pubspec.yaml:

environment:
  sdk: ">=2.17.0 <3.0.0"

Let’s start with a simple example. We want to define a set of routes for our app. We can do this by creating an enum:

enum AppRoutes {
  login,
  home,
  settings,
}

Now that we have our enum, we can enhance it to define our routes, views, etc…

enum AppRoutes {
  login('/login', LoginPage()),
  home('/home', HomePage()),
  settings('/settings', SettingsPage()); // Don't forget to add the semicolon here :)

  const AppRoutes(
	this.path,
	this.view,
  );

  final String path;
  final Widget view;
}

That’s it! Now our enum has a path and a view.

Combining enhanced enums with go_router ๐Ÿš€ #

Now that we have our enhanced enum, we can use it with go_router to define our routes.

Let’s first add the go_router dependency to our pubspec.yaml:

dependencies:
  go_router: ^5.1.5

And now let’s create our router:

final router = GoRouter(
  initialLocation: AppRoutes.login.path,
  routes: [
    GoRoute(
      path: AppRoutes.login.path,
      pageBuilder: (context, state) => MaterialPage<void>(
        key: state.pageKey,
        child: AppRoutes.login.view,
      ),
    ),
	// ... same for home/settings using AppRoutes.x.path and x.home.view
  ],
);

This already looks much better than the previous version, where we had to define the routes and views manually:

final router = GoRouter(
  initialLocation: '/login',
  routes: [
	GoRoute(
	  path: '/login',
	  pageBuilder: (context, state) => MaterialPage<void>(
		key: state.pageKey,
		child: LoginPage(),
	  ),
	),
	// ...
);

Anyway, we can make this even better by adding a method to our enum so that we can get the route directly from the enum just by calling AppRoutes.routeX.route:

enum AppRoutes {
  // ...
  GoRoute get route => GoRoute(
        path: path,
        pageBuilder: (context, state) => MaterialPage<void>(
          key: state.pageKey,
          child: view,
        ),
      );
}

This will allow us to simplify our router:

final router = GoRouter(
  initialLocation: AppRoutes.login.path,
  routes: [
	AppRoutes.login.route,
	AppRoutes.home.route,
	AppRoutes.settings.route,
  ],
);

I like it picasso! ๐ŸŽจ

Adding our router ๐Ÿšง #

Now that we have our router, we can add it to our app by updating our MaterialApp widget with a .router() constructor and adding a routerConfig parameter:

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Enums and Router demo',
      routerConfig: router,
    );
  }
}

Now that we have our router, we can easily navigate to those known routes, let’s test it by adding a text button to the login page:

//...
TextButton(
  onPressed: () => context.go(AppRoutes.home.path),
  child: Text('Home'),
),
//...

We can even simplify this by adding another method to our enum:

enum AppRoutes {
  // ...
  void go(BuildContext context) => context.go(path);
  // Additionall, we can add push and replace methods :)
  void push(BuildContext context) => context.push(path);
  void replace(BuildContext context) => context.replace(path);
}

And now we can easily navigate to our with the use of our new methods:

//...
TextButton(
  onPressed: () => AppRoutes.home.go(context),
  // onPressed: () => AppRoutes.home.push(context),
  // onPressed: () => AppRoutes.home.replace(context),
  child: Text('Home'),
),
//...

Now navigating seems much easier and less error-prone ๐Ÿ˜„

Conclusion ๐Ÿ“ #

In this post, I showed you how I like to use enhanced enums with go_router in order navigate in a more type-safe, less error-prone and in my opinion more readable way.
For sure there are some use cases where this approach might not be the best, but I think it’s worth trying it out and see if it fits your needs ๐Ÿ˜‰

I hope you enjoyed it and that you found it useful.
If you have any questions or suggestions, feel free to leave a comment below. ๐Ÿ˜„
Thanks for reading! ๐Ÿค“

The full source code with 94.5% test coverage ๐Ÿงช for this post is available here ๐Ÿ”
Sorry, I couldn’t get it to 100% ๐Ÿ˜… I don’t really know how to test the pageBuilder, but feel free to open a PR if you know how to do it ๐Ÿ˜‰

The pubspec.yaml file for this project uses the following dependencies ๐Ÿ“ฆ

dependencies:
  flutter:
    sdk: flutter
  go_router: ^5.1.5 # Used to define our router

dev_dependencies:
  flutter_test:
    sdk: flutter
  very_good_analysis: ^3.1.0 # Used to enforce very good practices ๐Ÿฆ„

References ๐Ÿ“š #

Author
Carlos Gutiรฉrrez