Themelis
Themelis

Reputation: 4255

How named routes in Flutter eliminate duplication?

I cannot understand the reason why someone should use named routes, with Navigator.pushNamed(), instead of the normal way with Navigator.push().

The tutorial page states that:

if we need to navigate to the same screen in many parts of our apps, this can result in code duplication. In these cases, it can be handy to define a “named route,” and use the named route for Navigation

Duplication

How will the duplication be generated when using simple routing and how it will can be eliminated with the use of named routes?

I fail to understand what is the difference of

Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );

from

Navigator.pushNamed(context, '/second');

in the context of duplication.

Upvotes: 11

Views: 9824

Answers (7)

qHack
qHack

Reputation: 332

For folks visiting this question in 2022. Flutter actually now recommends not using named routes

Note: Named routes are no longer recommended for most applications. For more information, see Limitations in the navigation overview page.

https://docs.flutter.dev/cookbook/navigation/named-routes

Upvotes: 3

Michel Feinstein
Michel Feinstein

Reputation: 14296

Consider you go with Navigator.push() in many widgets:

// inside widget A:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute()),
);

// inside widget B:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute()),
);

// inside widget C:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute()),
);

Now let say you need to change your App and the widget SecondRoute needs to receive a value on it's constructor. Now you have a problem, since you have multiple copies of the same code on several locations, you need to make sure you will update all of those copies, which can be tedious and error prone:

// inside widget A:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute(
      title: 'Title A',
  )),
);

// inside widget B:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute(
      title: 'Title B',
  )),
)),
);

// inside widget C:
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondRoute(
      title: 'Title A',     // ERROR! Forgot to change the variable after a copy/paste
  )),
)),
);

Now let's consider you go with named routes.

Firstly I would never recommend anyone to actually use the name directly for navigation, but instead use a static variable reference, this way if you need to change it in the future its way simpler and secure, as you can't forget to update it anywhere, like this:

class Routes {
  static const String second = '/second';
}

Another way is to have a reference inside the route itself, a static const String inside SecondRoute, so we can use it as SecondRoute.routeName. It's a matter of personal preference IMO.

Then your widgets will navigate using:

// inside widget A:
Navigator.pushNamed(context, Routes.second); // Routes.second is the same as '/second'

// inside widget B:
Navigator.pushNamed(context, Routes.second);

// inside widget C:
Navigator.pushNamed(context, Routes.second);

Now if you need to pass a parameter to SecondRoute upon creation you can do it in a centralized location using the MaterialApp onGenerateRoute, as this tutorial explains in more detail. Your code will be changed to:

// inside widget A:
Navigator.pushNamed(context, Routes.second, arguments: 'Title A');

// inside widget B:
Navigator.pushNamed(context, Routes.second, arguments: 'Title B');

// inside widget C:
// You can still make mistakes here, but the chances are smaller.
Navigator.pushNamed(context, Routes.second, arguments: 'Title C');
MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == Routes.second) {
      final String title = settings.arguments;

      return MaterialPageRoute(
        builder: (context) => SecondRoute(title: title),
      );
    }
  },
);

The amount of duplicated code is decreased, but on the other hand the onGenerateRoute code gets more complex as you make more routes, as all of their creation will be centralized there, so IMHO it's more about a personal preference then a general guideline.

Upvotes: 20

Ben Butterworth
Ben Butterworth

Reputation: 28888

Here is my beginner flutter thoughts:

It makes the code cleaner: Without declaring the routes at the level higher widgets, new screens will appear out of nowhere, in response to anything that happens in the app. It is much easier to understand the navigation skeleton/ structure when you declare the routes together, and even more so at a higher widget, especially for other developers. Of course, this doesn't help with understanding exactly when those routes are actually navigated to, but its a small improvement, and brings us back into the declarative paradigm. The hint provided by the declared routes will help a newer developer understand your navigation flow.

Upvotes: 2

0xCCY
0xCCY

Reputation: 555

Push and PushNamed have the similar effect, Push will switch to the route you specified while PushNamed will switch to the route with the route name specified.

What the Tutorial page means for duplication is duplication of code not duplication of routes.

For instance, you have a route where you would want to check whether the user is signed in and show the corresponding page

Using Push only: Page1:

//This is page 1....
 RaisedButton(
          child: Text('Go to second'),
          onPressed: () {
            if (user.state = "login") {
              Navigator.of(context).push(
               MaterialPageRoute(
                builder: (context) => SecondPage(),
              ),
             )
            }else{
              Navigator.of(context).push(
               MaterialPageRoute(
                builder: (context) => SecondPageAnonymous(),
               ),
              )
            }
          }
        )
....

In another page, Page2, you will need to repeat the same code:

//This is page 2....
 RaisedButton(
          child: Text('Go to second'),
          onPressed: () {
            if (user.state = "login") {
              Navigator.of(context).push(
               MaterialPageRoute(
                builder: (context) => SecondPage(),
              ),
             )
            }else{
              Navigator.of(context).push(
               MaterialPageRoute(
                builder: (context) => SecondPageAnonymous(),
               ),
              )
            }
          }
        )
....

With PushNamed, you just have to declare it once and you can basically reuse it over and over again.

In your onGenerateRoute:

onGenerateRoute: (settings) {
switch (settings.name) {
  case '/':
    return MaterialPageRoute(builder: (_) => FirstPage());
  case '/second':
    if (user.state = "login") {
      return MaterialPageRoute(
        builder: (_) => SecondPage()
      );
    }else{
      return MaterialPageRoute(
         builder: (_) => SecondPageAnonymous()
     );
    }

  default:
    return _errorRoute();
 }
},

Now in ANY pages in your project, you could do this:

 Navigator.of(context).pushNamed('/second')

Without needing to repeat the checking of sign in or even the error handling every time you used it. The obvious benefit is that you can stay consistent throughout the app by preventing duplicate code piece, instead of repeating it again and again.

Now, this however DOES NOT prevent duplicates of routes! There is no different between push and pushNamed in this context!

But since your routes are now named, you can do popUntil('/') easily to go back to the first instance of the route, instead of creating it again or PushReplacementNamed.

Upvotes: 4

Payam Zahedi
Payam Zahedi

Reputation: 855

for understanding why we should use Navigator.pushNamed instead Navigator.push first let's be familiar with Navigator methods. did you ever heart about Navigator.popUntil or Navigator.pushAndRemoveUntil? we use Navigator.popUntil when we want to pop in the stack to a specific route. if you check the documentation you can find that it's very easy to use these methods with the pushNamed method. also, check all methods in the documentation. when I try to understand routing in flutter this article was very useful for me. and as a disadvantage, it's very hard to handle parameters in this approach. you should create onGenerateRoute and handle parameters for each route.

Upvotes: 0

Stewie Griffin
Stewie Griffin

Reputation: 5638

If you use push(), you have to import the file in which SecondRoute is located every time you need to navigate to that screen. This is excessive code duplication for big projects that you need to navigate around the different screens.

If you use pushNamed(), you need to define the routes only once in your MaterialApp. Then, you can navigate to any screen from anywhere without repeating the same thing like you have to with push().

Another big reason to choose PushNamed() over the other one is to be able to build your own navigation system with it. You can decide whether or not routes are available for certain users even before they navigate to the screen.

Upvotes: 0

Jitesh Mohite
Jitesh Mohite

Reputation: 34250

The only advantage I can see using Navigate with named routes is to have routes declared inside your MaterialApp, so that developer can only be used assigned routes i.e widgets, pages,

If anyone uses other than that, It will give an error 'onUnknownRoute is called.'

Upvotes: 2

Related Questions