'Why I should use named routes?

I searched a lot about "what are benefits of using named route to navigate between screens". And I can't find any actual benefits, actually it has many disadvantages and annoying.

1. In flutter document, it said that named routes use to avoid code duplication.

For example, if I want to navigate to SecondRoute with String one argument, it change from this

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

to this

Navigator.pushNamed(context, SecondRoute.routeName, arguments: 'Some text');

and I need to register it in main file

MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == SecondRoute.routeName) {
      final String text = settings.arguments as String;
      return MaterialPageRoute(
        builder: (context) => SecondRoute(text),
      );
    }
  },
);

And if I have more routes, I need to handle and assign arguments for every routes. Isn't that more duplication and complexity?

Also setting.arguments has no-type, isn't that very bad?

2. You can't select which constructor to use when using pushNamed.

For example, I have two constructor default and otherConstructor

class SecondRoute extends StatelessWidget {
  static const String routeName = '/second';
  String? text;

  SecondRoute(this.text);

  SecondRoute.otherConstructor(String text) {
    this.text = 'Other Constructor: ' + text;
  }
}

How can I tell pushNamed which one I want to use.

I have an idea to pass constructor name as an argument and check it like this

Navigator.pushNamed(context, SecondRoute.routeName, arguments: ['default' or 'otherConstructor','Some text']);

In main file

MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == SecondRoute.routeName) {
      final args = settings.arguments as List<String>;
      if (args[0] == 'otherConstructor') {
        return MaterialPageRoute(
          builder: (context) => SecondRoute.otherConstructor(text),
        );
      } else if (args[0] == 'default') {
        return MaterialPageRoute(
          builder: (context) => SecondRoute(text),
        );
      }
    }
  },
);

But this is very very complicate and obviously not a good way to do.

3. Some anwsers in reddit and stackoverflow said that named route make code more centralize by keep every route in main file.

Surely centralization is good, but why not do it others way?

For example, keep all dart files that contain routes in new folder

enter image description here

Can someone enlighten me why most people use named route? Thanks for any help.



Solution 1:[1]

From my point of view, personally I think the root cause of your concern is you keep using pass the data around your screens using constructor or injection.

Reference from Official flutter docs: https://docs.flutter.dev/development/data-and-backend/state-mgmt/declarative

I am an iOS developer with UI kit so I think you has the same problem with me, that I resolved by changing my thinking flow.

Important thing : From the declaratively UI picture: UI = f(state).

For your problem (1):

  • I think we should not to pass the data around screens, you can use rxdart, provider, mobx, redux... or any state management. It will keep the data of for UI rendering and you no need to check it again in very long switch/if in main file. If you keep passing arguments so you can't build UI from state

For your problem (2):

  • I think it's the same problem with (1). Instead of using many constructors, you can create your state to reflecting it. So your screen(I mean the Widget reflecting your screen, not their sub widgets) can take data from state and no need to get it from arguments. Again, it about UI = f(state).

For your problem (3):

  • I definitely agree with you, keep it in another folder is a best choice.

Note: Personally right now I split widgets to 2 types: screen and Presentational widget.

  • Screen: Never pass arguments to it, it use the data from state(UI = f(state))
  • Presentational widget: can receive arguments in constructor.

The original idea is from the founder of redux (https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)

=> With this idea, It clearly resolved your problem (1) and (2). With (3) by split to another folder and import it should end with some lines in main file like this(without any process arguments on screen changing):

enter image description here

For the benefit:

Personally I think most advantage is:

1 - You can change the screen later without modify working code(open-closed principle): Assuming your client want to change the screen A to screen A', which has alot of navigate in your code.

Example: your app can go to login screen from 3 different logic: from splash, from press logout, from admin banned message.

If you use: MaterialPageRoute(builder: (context) => SecondRoute('Some text')), so you need to replace it many times(3 times in example) inside the code that work perfectly. The most dangerous thing is: maybe the logic "from admin banned message" is @ @ from other dev

If you usse: Navigator.pushNamed(context, SecondRoute.routeName, arguments: 'Some text'), you change the code in splited folder(that you mention in (3)) by redirect it to another screen(the new login scrren)

Things below I think it less important:

2- (just a note) If you build your project with web version, you will the route name in the url address, so it turn your page to get url.

3- Prevents alot of unnecessary import(example you can see in login, if you use routes, just import 1 file. but you use navigator directly you need to import many screens related: register, forgotpass....). Anyway, it can resolve by create something like screen_index.dart.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1