Difference between onGenerateRoute and routes in Flutter

NB (see comments below answer):

There is no bug, just bad usage by the issue reporter. It is caused because he didn't pass the settings object along to the new MaterialPageRoute returned by the onGenerateRoute method (see the issue's final comments). [...]

Original answer mentionning a "bug":

Without diving into any details those two properties do the same thing, but as @Alireza noted routes is checked first.

Also, using onGenerateRoute gives you a single place to add your custom business logic before pushing new routes (pages). For example, if you want to do some initializations.

routes property:

When a named route is pushed with Navigator.pushNamed, the route name is looked up in this map. If the name is present, the associated WidgetBuilder is used to construct a MaterialPageRoute that performs an appropriate transition, including Hero animations, to the new route.

onGenerateRoute property:

The route generator callback used when the app is navigated to a named route. ... This is used if routes does not contain the requested route.

IMPORTANT: What you really want to be aware of is a known bug in onGenerateRoute property.

The problem is that if you use onGenerateRoute to create named routes you won't be able to get the name of that route from RouteSettings object in your page. (Arguments attached to the settings object are fine though) In other words:

Widget build(BuildContext context) {
    ModalRoute.of(context).settings.name == null;       //bug
    ModalRoute.of(context).settings.arguments != null;  //ok

This may affect you in case you would like to know the name of the current route. For example, if you want to pop some screens:


Therefore, until this problem is resolved, I recommend using the routes: property.

routes is static and doesn't offer functionalities like passing an argument to the widget, implementing a different PageRoute etc, which is why onGenerateRoute exists.

In the given code, you'll find that how using onGenerateRoute property, you can parse an argument and send it over, which isn't possible with simple routes.

FooPage is navigated through routes and BarPage through onGenerateRoute.

Initial setup in MaterialApp.

void main() {
      routes: {
        '/': (_) => HomePage(), // You can also use MaterialApp's `home` property instead of '/'
        '/foo': (_) => FooPage(), // No way to pass an argument to FooPage.
      onGenerateRoute: (settings) {
        if (settings.name == '/bar') {
          final value = settings.arguments as int; // Retrieve the value.
          return MaterialPageRoute(builder: (_) => BarPage(value)); // Pass it to BarPage.
        return null; // Let `onUnknownRoute` handle this behavior.


class HomePage extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('HomePage')),
      body: Center(
        child: Column(
          children: [
              onPressed: () => Navigator.pushNamed(context, '/foo'),
              child: Text('Go to FooPage'),
              onPressed: () => Navigator.pushNamed(context, '/bar', arguments: 42), // Passing argument
              child: Text('Go to BarPage'),


class FooPage extends StatelessWidget {
  Widget build(_) => Scaffold(appBar: AppBar(title: Text('FooPage')));

And bar.dart:

class BarPage extends StatelessWidget {
  final int value;

  Widget build(_) => Scaffold(appBar: AppBar(title: Text('BarPage, value = $value')));

Screenshot (for reference)

enter image description here