Flutter: Get passed arguments from Navigator in Widget's state's initState

ada*_*rsh 5 flutter

I have a StatefulWidget which I want to use in named route. I have to pass some arguments which I am doing as suggested in https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments i.e.

Navigator.pushNamed(
      context,
      routeName,
      arguments: <args>,
    );
Run Code Online (Sandbox Code Playgroud)

Now, I need to access these argument's in the state's initState method as the arguments are needed to subscribe to some external events. If I put the args = ModalRoute.of(context).settings.arguments; call in initState, I get a runtime exception.

20:49:44.129 4 info flutter.tools I/flutter ( 2680): ??? EXCEPTION CAUGHT BY WIDGETS LIBRARY ????????????????????????????????????????????????????????????
20:49:44.129 5 info flutter.tools I/flutter ( 2680): The following assertion was thrown building Builder:
20:49:44.129 6 info flutter.tools I/flutter ( 2680): inheritFromWidgetOfExactType(_ModalScopeStatus) or inheritFromElement() was called before
20:49:44.130 7 info flutter.tools I/flutter ( 2680): _CourseCohortScreenState.initState() completed.
20:49:44.130 8 info flutter.tools I/flutter ( 2680): When an inherited widget changes, for example if the value of Theme.of() changes, its dependent
20:49:44.131 9 info flutter.tools I/flutter ( 2680): widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor
20:49:44.131 10 info flutter.tools I/flutter ( 2680): or an initState() method, then the rebuilt dependent widget will not reflect the changes in the
20:49:44.131 11 info flutter.tools I/flutter ( 2680): inherited widget.
20:49:44.138 12 info flutter.tools I/flutter ( 2680): Typically references to inherited widgets should occur in widget build() methods. Alternatively,
20:49:44.138 13 info flutter.tools I/flutter ( 2680): initialization based on inherited widgets can be placed in the didChangeDependencies method, which
20:49:44.138 14 info flutter.tools I/flutter ( 2680): is called after initState and whenever the dependencies change thereafter.
20:49:44.138 15 info flutter.tools I/flutter ( 2680): 
20:49:44.138 16 info flutter.tools I/flutter ( 2680): When the exception was thrown, this was the stack:
20:49:44.147 17 info flutter.tools I/flutter ( 2680): #0      StatefulElement.inheritFromElement.<anonymous closure> (package:flutter/src/widgets/framework.dart:3936:9)
20:49:44.147 18 info flutter.tools I/flutter ( 2680): #1      StatefulElement.inheritFromElement (package:flutter/src/widgets/framework.dart:3969:6)
20:49:44.147 19 info flutter.tools I/flutter ( 2680): #2      Element.inheritFromWidgetOfExactType (package:flutter/src/widgets/framework.dart:3285:14)
20:49:44.147 20 info flutter.tools I/flutter ( 2680): #3      ModalRoute.of (package:flutter/src/widgets/routes.dart:698:46)
20:49:44.147 21 info flutter.tools I/flutter ( 2680): #4      _CourseCohortScreenState.initState.<anonymous closure> (package:esk2/cohort_screen.dart:57:23)
Run Code Online (Sandbox Code Playgroud)

I do not want to put that logic in build method as build could be called multiple times and the initialization needs to happen only once. I could put the entire logic in a block with a boolean isInitialized flag, but that does not seem like the right way of doing this. Is this requirement/case not supported in flutter as of now?

len*_*enz 29

恕我直言,接受的应该是didChangeDependencies

late Object args; 

@override
void didChangeDependencies() {
  args = ModalRoute.of(context).settings.argument
  super.didChangeDependencies();
}

@override
Widget build(BuildContext context) {
   /// use args here 
}
Run Code Online (Sandbox Code Playgroud)

文档中提到了

该方法也会在 initState 之后立即调用。从此方法调用 BuildContext.dependOnInheritedWidgetOfExactType 是安全的。

您的错误代码中也提到了

基于继承的小部件的初始化可以放在 didChangeDependency 中,在 initState 之后以及此后每当依赖项发生变化时调用。


Mar*_*oso 16

我刚刚和你遇到了同样的问题,并提出了一个解决方案。除了使用 onGenerateRoute,您仍然可以使用 pushNamed Navigator 来传递参数,并且您仍然可以访问 initState 中的 ModalRoute 参数 - 方法如下:

1) 在 initState 中使用 future 来访问上下文。

  • 你可以这样做 Future.delayed(Duration.zero, () {} )
  • 这使您可以访问上下文,您还可以使用 this 在 initState 中执行 showDialog 之类的操作,因为您可以在构建方法之外访问此处的上下文。

2)使用提取参数 ModalRoute.of(context).settings.arguments

  • 在未来内部,提取参数并将它们存储在您在 initState 之前创建但显然仍在 State 对象中的已声明但未初始化的变量中。
  • 一旦你有了参数,你就可以对它们做任何你想做的事情,比如将变量传递给函数。
  • 重要提示:您必须在 future 函数体内使用变量,否则 Flutter 将跳过未来(按照其编程的方式)并首先完成外部的任何内容,因此您的 var 仍将返回 null,因为未来尚未决定给 var 一个值。

总之,它看起来像这样:

var = args;
_yourFunction(args) async {
// whatever you want to do
}

@override
  void initState() {
    super.initState();
    // future that allows us to access context. function is called inside the future
    // otherwise it would be skipped and args would return null
    Future.delayed(Duration.zero, () {
      setState(() {
        args = ModalRoute.of(context).settings.arguments;
      });
      print(args['id']);
      _yourFunction(args);
    });
  }

Run Code Online (Sandbox Code Playgroud)

  • Flutter Cookbook 建议使用 onGenerateRoute。onGenerateRoute 的存在就是为了这个目的。使用它比在复杂的国家黑客攻击中使用它更好。 (5认同)

Chu*_* Li 13

您可以使用新的 PageRoute 调用 push,而不是通过 pushNamed 发送参数。

假设您的参数类型称为 Argument。这是您的有状态小部件及其状态类的样子:

class YourStatefulWidget extends StatefulWidget {
    final Argument argument;

    YourStatefulWidget({
        @required this.argument,
    });

    @override
    State<StatefulWidget> createState() {
        return YourStatefulWidgetState();
    }
}

class YourStatefulWidgetState extends State<YourStatefulWidget> {

    @override
    initState() {
        super.initState();

        // Refer to your argument here by "widget.argument"

    }
}
Run Code Online (Sandbox Code Playgroud)

以下是使用 PageRoute 调用 push 的方法:

Navigator.of(context).push(MaterialPageRoute(builder: (context) => YourStatefulWidget(argument: Argument())));
Run Code Online (Sandbox Code Playgroud)


小智 9

您可以使用在构造函数中传递参数的命名路由。

   routes: {
    '/hello': (context) => Hello(
          argument: ModalRoute.of(context).settings.arguments,
        ),
  },
Run Code Online (Sandbox Code Playgroud)

然后在你的小部件中。

 class Hello extends StatefulWidget {
  final argument;

  Hello({this.argument});

  @override
  _HelloState createState() => _HelloState();
}
Run Code Online (Sandbox Code Playgroud)


psk*_*ink 7

使用如下MaterialApp.onGenerateRoute属性:

onGenerateRoute: (RouteSettings settings) {
  print('build route for ${settings.name}');
  var routes = <String, WidgetBuilder>{
    "hello": (ctx) => Hello(settings.arguments),
    "other": (ctx) => SomeWidget(),
  };
  WidgetBuilder builder = routes[settings.name];
  return MaterialPageRoute(builder: (ctx) => builder(ctx));
},
Run Code Online (Sandbox Code Playgroud)

现在您可以简单地使用NavigatorState.pushNamed

Navigator.of(context).pushNamed("hello", arguments: "world");
Run Code Online (Sandbox Code Playgroud)

在这里,您有一些测试Hello小部件:

class Hello extends StatelessWidget {
  final String greet;

  Hello(this.greet);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text(
          'hello $greet',
          textScaleFactor: 5.0,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 在下面的行中,如果不传递 `settings`,路由 URL 将不会出现在 Web 中: return MaterialPageRoute(builder: (ctx) =&gt; builder(ctx), settings: settings); (3认同)
  • 需要注意的是,每次构建后都会调用“didChangeDependency”,而不是“initState” (2认同)

小智 5

我用WidgetsBinding来做。它可以在initState内部调用,并且仅在 Build widget 完成渲染后调用一次。

@override
void initState() {
   super.initState();

   final widgetsBinding = WidgetsBinding.instance;
   widgetsBinding.addPostFrameCallback((callback) {
  if (ModalRoute.of(context).settings.arguments != null) {
    _currentIndex = ModalRoute.of(context).settings.arguments;
  }
 });
}
Run Code Online (Sandbox Code Playgroud)