Flutter中的命名路由如何消除重复?

时间:2019-04-01 22:12:12

标签: flutter

我不明白为什么有人使用Navigator.pushNamed()而不是Navigator.push() normal way 命名路由的原因。

tutorial page指出:

  

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

重复

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

我不明白

有什么区别
Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );

来自

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

重复上下文中

6 个答案:

答案 0 :(得分:8)

考虑在许多小部件中都使用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()),
);

现在让我们说您需要更改您的App,小部件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 String内部有一个SecondRoute,因此我们可以将其用作SecondRoute.routeName。这是国际海事组织的个人喜好。

然后,您的窗口小部件将使用以下内容进行导航:

// 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在集中的位置进行操作,例如tutorial explains in more detail。您的代码将更改为:

// 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代码随着您创建更多路线而变得更加复杂,因为它们的所有创建都将集中在此,因此恕我直言,这更多地取决于个人喜好然后是一般准则。

答案 1 :(得分:1)

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

如果使用pushNamed(),则只需在MaterialApp中定义一次路线。然后,您可以从任何地方导航到任何屏幕,而无需像使用push()那样重复相同的操作。

选择PushNamed()而非另一个Intent intent = new Autocomplete.IntentBuilder( AutocompleteActivityMode.OVERLAY, fields).setCountry("UK") .setHint("Search Pickup Location").setInitialQuery(pickup.getText().toString()) .build(this); startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE); 的另一个重要原因是能够使用它构建自己的导航系统。您甚至可以在某些用户导航到屏幕之前,确定路由是否对某些用户可用。

答案 2 :(得分:1)

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。

答案 3 :(得分:1)

这是我初学者的扑朔迷离的想法:

它使代码更简洁::如果不在高级小部件级别上声明路由,则新的屏幕将无处不在,以响应应用程序中发生的一切。当您一起声明路线时,更容易理解导航框架/结构,在更高的窗口小部件处尤其如此,尤其对于其他开发人员而言。当然,这 并不能帮助您准确地了解这些路线的实际导航时间,但这只是一个小小的改进,使我们回到了声明式范式中。声明的路线提供的提示将帮助新开发人员了解您的导航流程。

答案 4 :(得分:0)

使用具有命名路线的导航时,我看到的唯一好处是在MaterialApp中声明了路线,以便开发人员只能使用已分配的路线,即小部件,页面,

如果有人使用其他方法,则会给出错误'onUnknownRoute被调用。'

答案 5 :(得分:0)

为了理解为什么我们应该使用Navigator.pushNamed而不是Navigator.push,让我们先熟悉Navigator方法。您是否对Navigator.popUntilNavigator.pushAndRemoveUntil感兴趣? 当我们想将堆栈弹出到特定路径时,我们使用Navigator.popUntil。如果查看文档,您会发现将这些方法与pushNamed方法一起使用非常容易。另外,请检查documentation中的所有方法。当我尝试了解颤动的this路由时,对我来说非常有用。 作为缺点,这种方法很难处理参数。您应该创建onGenerateRoute并为每个路由处理参数。