7

我不明白为什么有人应该使用命名路由, with Navigator.pushNamed(),而不是正常的 方式with Navigator.push()

教程页面指出:

如果我们需要在应用程序的许多部分导航到同一个屏幕,这可能会导致代码重复。在这些情况下,定义“命名路由”并使用命名路由进行导航会很方便

复制

使用简单路由时如何生成重复,使用命名路由如何消除重复?

我不明白有什么区别

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

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

重复的情况下

4

6 回答 6

16

考虑您使用Navigator.push()许多小部件:

// 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()),
);

现在假设您需要更改您的应用程序并且小部件SecondRoute需要在其构造函数上接收一个值。现在您遇到了一个问题,因为您在多个位置有相同代码的多个副本,您需要确保更新所有这些副本,这可能很乏味且容易出错:

// 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
  )),
)),
);

现在让我们考虑使用命名路线。

首先,我绝不会建议任何人直接使用该名称进行导航,而是使用static变量引用,这样如果您将来需要更改它,它的方式会更简单、更安全,因为您不会忘记在任何地方更新它,像这样:

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

另一种方法是在路由本身内部有一个引用,一个static const Stringinside SecondRoute,所以我们可以将它用作SecondRoute.routeName。这是个人喜好IMO的问题。

然后您的小部件将使用以下方式导航:

// 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);

现在,如果您需要SecondRoute在创建时传递一个参数,您MaterialApp onGenerateRoute可以使用. 您的代码将更改为:

// 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),
      );
    }
  },
);

重复代码的数量减少了,但另一方面,onGenerateRoute当您制作更多路线时,代码变得更加复杂,因为它们的所有创建都将集中在那里,所以恕我直言,这更多的是个人偏好而不是一般准则。

于 2020-04-29T23:31:17.467 回答
3

Push 和 PushNamed 的作用类似,Push 会切换到你指定的路由,而 PushNamed 会切换到指定路由名称的路由。

教程页面对重复的意义是代码的重复而不是路线的重复。

例如,您有一条路线,您希望在其中检查用户是否已登录并显示相应的页面

仅使用推送:第 1 页:

//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(),
               ),
              )
            }
          }
        )
....

在另一个页面 Page2 中,您将需要重复相同的代码:

//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(),
               ),
              )
            }
          }
        )
....

使用 PushNamed,您只需声明一次,您基本上可以一遍又一遍地重用它。

在您的 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();
 }
},

现在在您项目的任何页面中,您都可以这样做:

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

无需在每次使用时重复检查登录甚至错误处理。显而易见的好处是,您可以通过防止重复代码片段来保持整个应用程序的一致性,而不是一次又一次地重复它。

现在,但这并不能防止路线重复!在这种情况下,push 和 pushNamed 没有区别!

但是由于您的路由现在已命名,您可以轻松地执行 popUntil('/') 以返回到路由的第一个实例,而不是再次创建它或 PushReplacementNamed。

于 2020-05-05T06:33:02.323 回答
2

我可以看到使用带有命名路由的 Navigate的唯一优点是在 MaterialApp 中声明了路由,这样开发人员就只能使用分配的路由,即小部件、页面、

如果有人使用除此之外的其他方法,则会给出错误“调用了 onUnknownRoute”。

于 2019-09-03T14:43:15.527 回答
2

这是我初学者颤振的想法:

它使代码更清晰:无需在更高级别的小部件上声明路由,新屏幕将突然出现,以响应应用程序中发生的任何事情。当您一起声明路线时,更容易理解导航骨架/结构,在更高的小部件上更容易理解,尤其是对于其他开发人员。当然,这无助于准确了解这些路线实际导航到的时间,但它是一个小的改进,并将我们带回到声明性范式。声明的路由提供的提示将帮助新开发人员了解您的导航流程。

于 2020-05-05T07:45:16.117 回答
0

为了理解为什么我们应该使用 Navigator.pushNamed 而不是 Navigator.push 首先让我们熟悉 Navigator 方法。你有没有想过Navigator.popUntilNavigator.pushAndRemoveUntil?当我们想在堆栈中弹出到特定路由时,我们使用 Navigator.popUntil。如果您查看文档,您会发现将这些方法与 pushNamed 方法一起使用非常容易。另外,检查文档中的所有方法。当我尝试理解 Flutter 中的路由时,这篇文章对我非常有用。作为一个缺点,在这种方法中处理参数非常困难。您应该为每条路线创建 onGenerateRoute 并处理参数。

于 2020-05-04T12:48:03.723 回答
0

如果使用push(),则每次需要导航到该屏幕时都必须导入 SecondRoute 所在的文件。对于需要在不同屏幕上导航的大型项目来说,这是过多的代码重复。

如果使用pushNamed(),则只需在 MaterialApp 中定义一次路由。然后,您可以从任何地方导航到任何屏幕,而无需重复使用push().

选择另一个的另一个重要原因PushNamed()是能够用它构建自己的导航系统。您甚至可以在某些用户导航到屏幕之前决定他们是否可以使用路线。

于 2020-04-30T13:20:40.013 回答