【问题标题】:Navigator pass arguments with pushNamed导航器使用 pushNamed 传递参数
【发布时间】:2018-11-14 16:05:55
【问题描述】:

之前可能有人问过,但我找不到,但是如何将参数传递给命名路由?

这就是我构建路线的方式

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')

但是如果我的小部件是 new Presentation(arg1, arg2, arg3) 呢?

【问题讨论】:

标签: flutter


【解决方案1】:

不需要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(...);
}

【讨论】:

  • 为了回调参数,我应该这样写:text(arguments['path']) text(arguments['path2']) 文本(参数['path3']) ?并且可以将参数存储在变量中,所以我只调用类似 loadedArgument.path,loadedArgument.path2,loadedArgument.path3 的东西?
  • 如果在 initState() 中我需要传递的参数来调用 api 怎么办?那里还没有上下文。如何解决这个问题?谢谢
  • @cwhisperer 您可以尝试使用 WidgetsBinding.instance.addPostFrameCallback((_) {doContextStuff(context); });在 initState()
  • 最终参数 = ModalRoute.of(context).settings.arguments as Map;这给出了一个错误,即 null 类型不是类型转换中类型映射的子类型,我不知道为什么。
  • @Adnanhaider 我遇到了同样的问题。我认为此答案中的代码假定 settings.arguments 不能为空。在我的情况下它可以。所以as Map 试图将 null 转换为 Map,这会导致错误。我首先通过从该行中删除as Map 并添加一个空检查final arguments = ModalRoute.of(context)!.settings.arguments; 来解决这个问题。然后if (arguments != null) 可以将参数转换为 Map。
【解决方案2】:

pushNamed() 现在支持 merged pull request 的参数。如果你迫不及待,请切换到频道master(flutter 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,
    );
}

“参数”可以是任何对象,例如一张地图。

【讨论】:

  • 你能解释一下 FooRoute 是什么意思吗?
  • @UndercoverCoder FooRoute 是第二个/目标屏幕。
  • 出于某种原因,我的论点没有作为地图进行。始终为空
  • MaterialPageRoute中的settings参数很重要,没有这个,你在使用的时候不能得到正确的参数。
【解决方案3】:

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

因此,在小部件中,您将拥有一个新屏幕:

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

您的构造函数中根本不需要这些参数。相反,您可以像其他人所说的那样在 SomePage 小部件中将它们拉出来;即通过:

final arg = ModalRoute.of(context)!.settings.arguments as Map;

并且可以在 SomePage 构建中分配它们,例如:

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

使用您输入的任何键。

如果您希望 MaterialApp 处理它,那么您可以使用 onGenerateRoute 方法。这让我永远注意到参数直接转到推送的小部件。对我来说,这是违反直觉的。

【讨论】:

  • 谢谢。 as Map 执行什么功能?
  • 此函数将您的参数解释为 Map
【解决方案4】:

参数可以是任何对象,你可以创建一个数组,如你所见:

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

并访问路由器类。

【讨论】:

  • 这是一张地图,不是一个数组
【解决方案5】:

传递参数:

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"], );

【讨论】:

    【解决方案6】:

    基本上你有两个选择:

    • 使用一些 3rd 方包进行路由 - 我认为最好的是 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]);
    ...
    

    【讨论】:

    • 哇,感谢您的快速回复,但是如果我可以传入一个字符串,如果我想将对象发送到我的演示小部件,我仍然可以传入一个 json 字符串?
    • 这是可能的,但我相信将对象保存在某处会是一种更好的方法,(我更喜欢 redux 存储)并通过路由器传递所需的最少数据量( id ),然后使用从存储中获取对象id 在您的页面内。这种方式将更具前瞻性
    【解决方案7】:

    使用从 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;
    

    【讨论】:

    • type null 不是类型转换中类型映射的子类型,我得到了错误
    【解决方案8】:

    对于带有多个参数或动态对象的named Routes,您需要执行以下操作(这是 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);
    

    【讨论】:

      【解决方案9】:

      // 我们要导航的主屏幕

      Navigator.pushNamed(
                                    context,
                                   "/ScreenName",
                                    arguments: {
                                      'id': "20" 
                                    });
      

      我也遇到了与使用 NavigationRoutes 相同的问题

      class NavigationRoutes {
        static Route<dynamic> generateScreenRoute(RouteSettings settings) {
         // TODO below we are able to get Arguments list
          Map args = settings.arguments;
          
          switch (settings.name) {
            
            case "Screen Name": 
              return MaterialPageRoute(builder: (context) => ScreenName(args)); 
            // TODO pass above argument here and get in the form of Constructor in Screen 
            //   Name Class
          }
      
      }
      

      【讨论】:

        【解决方案10】:

        最好将参数包装在一个对象中以避免容易出错的代码。

        以下是有关如何在颤振飞镖中实现它的工作示例。

        发送

        Navigator.of(context).pushNamed(Routes.submitSuccess, arguments: successModel);
        

        接收

          case Routes.submitSuccess:
            if (settings.arguments is SubmitSuccessModel) {//Check for instance here
              return CupertinoPageRoute(
                  builder: (_) =>
                      SubmitSuccessPage(successModel: settings.arguments));
            }
        

        模型对象

        class SubmitSuccessModel {
          SubmitSuccessModel(
              {this.title, this.desc, this.actionButtonName, this.widget});
          String title;
          String desc;
          String actionButtonName;
          Widget widget;
        }
        
        final SubmitSuccessModel successModel = SubmitSuccessModel(
          title: 'Title',
          desc: 'Desc',
          actionButtonName: 'Done',
        );
        

        【讨论】:

          【解决方案11】:

          注意:new routing options may be available soon.


          这可能是一种选择吗?

          使用 push 和 RouteSettings 参数指定命名路由。

          通过这种方式,您可以以类型安全的方式直接将任何类型(包括对象)的参数传递给您的目标 Widget,并使用 arguments 跳过。你不需要create a single-use throwaway arguments class 也不需要地图。

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

          推送 + 路由设置

          要将push 与命名路由一起使用,请将RouteSettings 参数与路由名称一起使用。

          示例:用户登录Page1,现在您希望将push 从Page1 变为Page2

          在Navigator.push 调用中直接在Page1 内部将User 对象(loggedInUser) 传递给Page2,并将RouteSettings 参数与您的路由名称(/page2) 一起使用。

          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;
          

          【讨论】:

            【解决方案12】:

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

            您的构造函数中根本不需要这些参数。相反,您可以像其他人所说的那样将它们拉出小部件;即通过:

            final arg = ModalRoute.of(context)!.settings.arguments as Map;
            

            并且可以像这样分配它们:

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

            使用您输入的任何键。

            如果您希望 MaterialApp 处理它,那么您可以使用 onGenerateRoute 方法。这让我永远注意到参数直接转到推送的小部件。对我来说,这是违反直觉的。

            【讨论】:

              【解决方案13】:

              screen_1.dart

              Navigator.of(context).pushNamed('/screen2',arguments: {'var1': val1 ,'var2': val2, "var3": val3 ,"var4" : val4}); //sending of the values to route_generator.dart
              

              route_generator.dart

              class RouteGenerator {
                static Route<dynamic> generateRoute(RouteSettings settings) //from screeen_1.dart
              {
                  // Getting arguments passed in while calling Navigator.pushNamed
                  Map args = settings.arguments;
              
              switch (settings.name) {
              case '/screen2':
                      if (args is Map) {
                        return MaterialPageRoute(
                          builder: (_) => screen_2(args),
                        );
                      }
                      return _errorRoute(); //optional written in a scaffold when any error arrises
              default:
              return _errorRoute(); //optional written in a scaffold when any error arrises
              
              }
              

              screen_2.dart

              screen_2(Map args) //follow the params from the route_generator.dart
              {
                return Scaffold(
                  body: FutureBuilder(
                      future: _screen_2EndpointProvider.fetchScreen_2(args['var1'], args["var2"], args['var3'], args['var4']),  //API call inside which it called the args 
                      builder: (context, snapshot) {
                        return snapshot.hasData
                            ? screen_2(param: snapshot.data)
                            : Center(child: CircularProgressIndicator());
                      }),
                );
              }
              

              我试图提供一个简单的例子,如果它对任何人都有帮助,因为经过多次试验后我遇到了同样的错误,它得到了解决。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-12-21
                • 2012-06-08
                • 2010-11-04
                • 1970-01-01
                • 2013-10-04
                • 1970-01-01
                • 2020-07-29
                • 2020-07-12
                相关资源
                最近更新 更多