使用pushNamed在导航器中传递参数

72

可能之前已经有人问过了,但我找不到答案,那么如何将参数传递给路由名称呢?

这是我构建路由的方式

Widget build(BuildContext context) {
    return new Navigator(
      initialRoute: 'home/chooseroom',
      onGenerateRoute: (RouteSettings settings) {
        WidgetBuilder builder;
        switch (settings.name) {
          case 'home/chooseroom':
            // navigates to 'signup/choose_credentials'.
            builder = (BuildContext _) => new ChoosePage();
            break;
          case 'home/createpage':
            builder = (BuildContext _) => new CreateRoomPage();
            break;
          case 'home/presentation':
            builder = (BuildContext _) => new Presentation();
            break;
          default:
            throw new Exception('Invalid route: ${settings.name}');
        }
        return new MaterialPageRoute(builder: builder, settings: settings);
      },
    );

这是如何调用它的方法:Navigator.of(context).pushNamed('home/presentation')

但如果我的widget是new Presentation(arg1, arg2, arg3)怎么办?


2
看起来它在开箱即用的情况下不受支持:https://github.com/flutter/flutter/issues/6225 - diegoveloper
16个回答

135

无需使用onGenerateRoute,只需使用

var exampleArgument = 'example string';

Navigator.pushNamed(
    context,
    '/otherscreen',
    arguments: {'exampleArgument': exampleArgument},
);

并按照以下方式提取参数:

@override
Widget build(BuildContext context) {
    final arguments = (ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{}) as Map;

    print(arguments['exampleArgument']);

    return Scaffold(...);
}

1
为了回调参数,我应该像这样编写:text(arguments['path']) text(arguments['path2']) text(arguments['path3'])? 并且可以将参数存储在变量中,所以我只需这样调用loadedArgument.pathloadedArgument.path2loadedArgument.path3 - Zeffry Reynando
3
如果在initState()中需要传递参数进行api调用,而此时上下文(context)还不可用,应该怎么解决?谢谢。 - cwhisperer
2
@cwhisperer 你可以尝试在 initState() 中使用 WidgetsBinding.instance.addPostFrameCallback((_) {doContextStuff(context);}); - madlick71
3
最终参数 = ModalRoute.of(context).settings.arguments as Map; 这行代码会报错,错误为 "type null is not a subtype of type map in type cast" ,我不知道为什么。 - Adnan haider
3
@Adnanhaider 我有同样的问题。我认为这个答案中的代码假定settings.arguments不可能为空,而在我的情况下可能为空。所以 as Map尝试将空值转换为Map,导致错误。我先通过从该行中删除as Map并添加一个空值检查final arguments = ModalRoute.of(context)!.settings.arguments;来解决这个问题。然后,如果arguments != null,你可以将arguments转换为Map。 - Rivers Cuomo
显示剩余2条评论

45

pushNamed()现在支持参数,自从这个合并的拉取请求。如果你等不及,请切换到渠道masterflutter channel master,然后可能跟着flutter upgrade)。

如何发送消息

    Navigator.pushNamed(ctx, '/foo', arguments: someObject);

如何接收

...
    return MaterialApp(
        ...
        onGenerateRoute: _getRoute,
        ...
    );
...

Route<dynamic> _getRoute(RouteSettings settings) {
    if (settings.name == '/foo') {
        // FooRoute constructor expects SomeObject
        return _buildRoute(settings, new FooRoute(settings.arguments));
    }

    return null;
}

MaterialPageRoute _buildRoute(RouteSettings settings, Widget builder) {
    return new MaterialPageRoute(
        settings: settings,
        builder: (ctx) => builder,
    );
}

“arguments”可以是任何对象,例如一个映射。


请问您能否解释一下FooRoute的含义是什么? - UndercoverCoder
2
@UndercoverCoder FooRoute 是第二个/目标屏幕。 - wiradikusuma
1
由于某些原因,我的参数没有作为映射传递。始终为空。 - Michael K Madison
MaterialPageRoute中的settings参数非常重要,如果没有它,在使用时无法获取正确的参数。 - JChen___

14

作为 Flutter 的新手,我花了一些时间才注意到这一点。但是,使用 Navigator.pushNamed 添加的参数会直接发送到您推送的小部件,而不是用于路由的 MaterialApp。

因此,在你所推送的小部件中,你将拥有一个新的屏幕:

Navigator.pushNamed(
   context,
   SomePage.routeName,
   arguments: {
      'v1': 'data1',
      'v2': 'data2',
      'v3': 'data3',
   },
)

你不需要在构造函数中使用这些参数。相反,像其他人建议的那样,在 SomePage widget 中提取它们,即通过:


你不需要在构造函数中使用那些参数,而是像其他人建议的那样,在 SomePage 小部件中提取它们,具体方式是通过:
final arg = ModalRoute.of(context)!.settings.arguments as Map;

并且可以在类似 SomePage 的构建中分配它们:

randomVar1 = arg['v1'];
randomVar2 = arg['v2'];
randomVar3 = arg['v3'];

使用任何您放置的键。

如果您希望MaterialApp进行处理,则可以使用onGenerateRoute方法。我花了很长时间才注意到参数直接传递给推送的小部件,这对我来说很不直观。


谢谢。as Map函数有什么作用? - sj_959
此函数将您的参数解释为Map。 - Richard Sipher

9

参数可以是任何对象,您可以像下面这样制作一个数组:

Navigator.of(context).pushNamed('/upload', arguments: {'_imagePath':_imagePath,
      'num_docfiscal':num_docfiscal,'dta_docfiscal':dta_docfiscal});

以及访问路由器类。


1
这是一个映射,不是一个数组。 - cs guy

7
基本上你有两个选择:
  • 使用一些第三方路由包 - 我认为最好的是Fluro
  • 利用onGenerateRoute。这个选项限制了你可以传递的参数(字符串/数字)。

要使用第二个选项,假设你想传递三个参数:Navigator.of(context).pushNamed('home/presentation:arg1:1337:hello')

MaterialApp ( 
         ... ,
         onGenerateRoute: handleRoute,
         routes:... , ) 

Route<dynamic> handleRoute(RouteSettings settings) {

    WidgetBuilder builder;

    final List<String> uri = settings.name.split('/');

    if (uri[0].startsWith('home')) {

      // handle all home routes:
      if(uri[1].startsWith('presentation:'){
         // cut slice by slice
         final String allArgs = 
               uri[1].substring('presentation:'.length);
         final List<String> args = allArgs.split(':');

          // use your string args
         print(args[0]);             // prints "arg1"
         int x = int.parse(args[1]); // becomes 1337
         print(args[2]);             // prints "hello"

         builder = (ctx)=> Presentation(args[0],args[1],args[2]);
...

1
哇,谢谢您的快速回复。但是,如果我可以传递一个字符串,那么如果我想将对象发送到我的演示小部件,我仍然可以传递一个JSON字符串吗? - Syph
是可以实现,但我认为更好的方式是在某个地方保存对象(我的首选是redux存储),然后通过路由传递最少量所需数据(ID),并使用页面内的ID从存储中获取这些对象。 这种方式将更具未来性。 - Buffer Underflow

5

针对从WidgetA到WidgetB的简单导航,需要采用以下步骤:

  1. 在MaterialApp主组件中定义路由:
    return MaterialApp(
       routes: {
           '/routeAB': (context) => WidgetB(),
       },
  1. 在WidgetA中使用pushNamed方法导航到WidgetB:
  onTap: () { 
     Navigator.pushNamed(context, '/routeAB', 
       arguments: {
          'arg1': val1,
          'arg2': val2,
          ...
  1. 获取WidgetB中的参数:
    Map args = ModalRoute.of(context).settings.arguments;

2
类型null不是类型映射的子类型,在类型转换中出现了错误,这是我得到的错误。 - Adnan haider

2

传递参数:

Navigator.pushNamed(YourScreen.routeName, arguments: {"title":myTitle, "user_name":userName});

提取参数:
        Map<String, dynamic> arguments = new Map<String, dynamic>.from(settings.arguments);
        page = MyRecordingScreen(title: arguments["title"], tags: arguments["user_name"], );

2
注意: 新的路由选项可能很快就会推出

这是一种可能的选择吗?

使用带有指定命名路由的RouteSettings参数的push

这样,您可以以类型安全的方式直接传递任何类型(包括对象)的参数到目标小部件,并跳过使用arguments。 您不需要创建一个单次使用的丢弃参数类或Map。

push中的RouteSettings可以向导航堆栈提供命名Route,您可以搜索/在将来的路由决策中使用它,就像使用pushNamed一样。

Push + RouteSettings

要使用带有命名路由的push,请使用带有路由名称的RouteSettings参数。

例如:用户登录Page1,现在您想要将他们从Page1推送到Page2

Page1中直接在Navigator.push调用中传递User对象(loggedInUser),并使用带有路由名称(/page2)的RouteSettings参数。

Navigator.push(context, MaterialPageRoute(
  builder: (context) => Page2(user: loggedInUser),
  settings: RouteSettings(name: '/page2')
));

在Page2小部件中,您可以直接使用User对象。
class Page2 extends StatefulWidget {
  final User loggedInUser;

  Page2(this.loggedInUser);

  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  User loggedInUser;
  @override
  void initState() {
    super.initState();
    loggedInUser = widget.loggedInUser;
    print(loggedInUser.name);
  }
  
  @override
  Widget build(BuildContext context) {
  }
}

以后您可以使用/page2路由名称。例如,如果您在/page3处并且想要popUntil(context, ModalRoute.withName('/page2')),那么这是允许的。
底层原理
Flutter的Navigator类显示pushNamed使用push+ routeNamed,而routeNamed使用RouteSettings
Future<T> pushNamed<T extends Object>(
    String routeName, {
    Object arguments,
  }) {
    return push<T>(_routeNamed<T>(routeName, arguments: arguments));
  }

Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {
final RouteSettings settings = RouteSettings(
      name: name,
      arguments: arguments,
    );
    Route<T> route = widget.onGenerateRoute(settings) as Route<T>;
return route;

顶级!!对我来说是最好的解决方案,谢谢。 - Shadros

1
对于具有多个参数或动态对象的命名路由,您需要按照以下方式进行操作(这是MVVM模式示例):
导航器:
void navigateEditParty(int index) {
    _navigationService.navigateTo(PartyEditRoute,
        arguments: {"hunter": hunter, "index": index});
  }

路由器:

case PartyEditRoute:
  Map args = settings.arguments;
  return _getPageRoute(
    routeName: settings.name,
    viewToShow: PartyEditView(
      hunter: args["hunter"],
      index: args["index"],
    ),
  );

类:

PartyEditView({Key key, this.hunter, this.index}) : super(key: key);

0

这就是如何发送参数/参数-

ElevatedButton(
                      onPressed: () {
                        Navigator.push(
                            context,
                            MaterialPageRoute(
                                builder: (context) => NewPresentation(
                                      id: '101',
                                      name: 'Unnamed',
                                    )));
                      },
                      child: const Text('Submit'))

这就是如何接收 -

class NewPresentation extends StatefulWidget {
  final String id, name;
  
  const NewPresentation({Key? key, required this.id, required this.name}) : super(key: key);
  @override
  State<NewPresentation> createState() => _NewPresentationState();
}

class _NewPresentationState extends State<NewPresentation> {
  Widget build(BuildContext context) {
    return Text(widget.id + widget.name);
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接