我有一个StatefulWidget
,我想在命名路由中使用它。我必须按照https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments中的建议,即
Navigator.pushNamed(
context,
routeName,
arguments: <args>,
);
现在,我需要在状态的initState
方法中访问这些参数,因为需要这些参数来订阅某些外部事件。如果将args = ModalRoute.of(context).settings.arguments;
调用放在initState
中,则会收到运行时异常。
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)
我不想将此逻辑放入build
方法中,因为build
可以被多次调用,并且初始化只需发生一次。我可以将整个逻辑放在带有boolean isInitialized标志的块中,但这似乎并不是正确的方法。
到目前为止,此要求/案例是否不受支持?
答案 0 :(得分:9)
我和您遇到了同样的问题,并提出了解决方案。代替使用onGenerateRoute,您仍然可以使用pushNamed Navigator来传递参数,并且仍然可以在initState中访问ModalRoute参数-方法如下:
1)使用initState中的future获得对上下文的访问。
Future.delayed(Duration.zero, () {} )
2)使用ModalRoute.of(context).settings.arguments
全部看起来像这样:
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);
});
}
答案 1 :(得分:2)
像这样使用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));
},
现在您可以简单地使用NavigatorState.pushNamed
:
Navigator.of(context).pushNamed("hello", arguments: "world");
这里有一些测试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,
),
),
);
}
}
答案 2 :(得分:1)
恕我直言,接受的应该是 didChangeDependencies
。
late Object args;
@override
void didChangeDependencies() {
args = ModalRoute.of(context).settings.argument
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
/// use args here
}
它在 docs
中提到 <块引用>这个方法也会在 initState 之后立即调用。这是安全的 从此方法调用 BuildContext.dependOnInheritedWidgetOfExactType。
您的错误代码中也提到了这一点
<块引用>基于继承的widget的初始化可以放在didChangeDependencies中 在 initState 之后以及每当依赖项发生变化时调用 此后。
答案 3 :(得分:0)
您可以通过新的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"
}
}
这是您使用PageRoute调用推送的方式:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => YourStatefulWidget(argument: Argument())));
答案 4 :(得分:0)
如果routeName不能导入太多,这对我来说是个好方法
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MySecondaryPage(requiredAttrib: myValue),
),
);
然后在您的小部件中,显然这只是在initState上使用TextEditingController的愚蠢用例:
class MySecondaryPage extends StatefulWidget {
final requiredAttrib;
MySecondaryPage({this.requiredAttrib});
@override
_MySecondaryPageState createState() => _MySecondaryPageState();
}
class _MySecondaryPageState extends State<MySecondaryPage> {
TextEditingController _myController = TextEditingController();
@override
void initState() {
super.initState();
_myController.text = widget.requiredAttrib;
...
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _myController,
...
);
}
}
答案 5 :(得分:0)
您可以使用在构造函数中传递de参数的命名路由。
routes: {
'/hello': (context) => Hello(
argument: ModalRoute.of(context).settings.arguments,
),
},
然后在您的窗口小部件中。
class Hello extends StatefulWidget {
final argument;
MySecondaryPage({this.argument});
@override
_MySecondaryPageState createState() => _MySecondaryPageState();
}
答案 6 :(得分:0)
我使用 WidgetsBinding 来实现。它可以在 initState 内部调用,并且只会在 Build widgets 渲染完成后调用一次。
@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;
}
});
}