未处理的异常:在 _ScreenState.initState() 完成之前调用了 inheritFromWidgetOfExactType(_LocalizationsScope) 或 inheri
Posted
技术标签:
【中文标题】未处理的异常:在 _ScreenState.initState() 完成之前调用了 inheritFromWidgetOfExactType(_LocalizationsScope) 或 inheritFromElement()【英文标题】:Unhandled Exception: inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before _ScreenState.initState() completed 【发布时间】:2019-10-17 02:28:41 【问题描述】:我正在调用初始方法以使用 initState 从 API 加载数据。但这导致我出错。这是错误:
Unhandled Exception: inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before _ScreenState.initState() completed.
When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget.
我的代码是:
@override
void initState()
super.initState();
this._getCategories();
void _getCategories() async
AppRoutes.showLoader(context);
Map<String, dynamic> data = await apiPostCall(
apiName: API.addUser,
context: context,
parameterData: null,
showAlert: false,
);
if(data.isNotEmpty)
AppRoutes.dismissLoader(context);
print(data);
else
AppRoutes.dismissLoader(context);
【问题讨论】:
您的 _getCategories 是否在构建器方法中?你是如何获得上下文的? 【参考方案1】:您需要在initState
完成后调用_getCategories
。
@override
void initState()
super.initState();
Future.delayed(Duration.zero, ()
this._getCategories();
);
// Could do this in one line: Future.delayed(Duration.zero, this._getCategories);
此外,您可以使用 addPostFrameCallback 以不同的方式执行此操作。 为了使这项任务更容易,您可以创建一个要添加到 StatefulWidgets 的 mixin。
mixin PostFrameMixin<T extends StatefulWidget> on State<T>
void postFrame(void Function() callback) =>
WidgetsBinding.instance?.addPostFrameCallback(
(_)
// Execute callback if page is mounted
if (mounted) callback();
,
);
然后,你只需要将这个 mixin 插入你的页面,就像这样:
class _MyPageState extends State<MyPage> with PostFrameMixin
@override
void initState()
postFrame(_getCategories);
super.initState();
【讨论】:
谢谢,它成功了。你能解释一下这是如何工作的吗? 这是一个安全的解决方案吗?如果 init 还没有完成怎么办? 我认为对 getCategories 使用 didChangeDependencies 而不是 init state 是这个答案的更好选择.....因为它在 init state 完成后被调用。【参考方案2】:使用在 initState 之后调用的 didChangeDependencies 方法。
你的例子:
@override
void initState()
super.initState();
@override
void didChangeDependencies()
super.didChangeDependencies();
this._getCategories();
void _getCategories() async
// Omitted for brevity
// ...
【讨论】:
这可能导致多次调用 _getCatergories(),因为 didChangeDependencies() 被多次调用。所以如果 _getCatergories() 涉及到网络调用或者刷新状态,那就很糟糕了。 这不仅适用于这种情况,也适用于其他问题***.com/questions/60363665/…【参考方案3】:Adding a frame callback 可能比使用零持续时间的 Future.delayed
更好 - 它更明确和清楚地了解正在发生的事情,而这种情况正是框架回调的设计目的:
@override
void initState()
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async
_getCategories();
);
【讨论】:
【参考方案4】:另一种方法是将它放在PostFrameCallback
中,它位于initState
和Build
之间,这样我们就可以确定initState
已经完成了。
@override
void initState()
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
super.initState();
getData() async
【讨论】:
【参考方案5】:解决这个问题的方法很多,重写initState
方法:
@override
void initState()
super.initState();
// Use any of the below code here.
使用SchedulerBinding
mixin:
SchedulerBinding.instance!.addPostFrameCallback((_)
// Call your function
);
使用Future
类:
Future(()
// Call your function
);
使用Timer
类:
Timer(()
// Call your function
);
【讨论】:
【参考方案6】:我认为最好的解决方案是使用 Widget 构建中的上下文。并在构建后使用树中的上下文粘贴方法 _getCategories(context)。 所以小部件树没有问题。
【讨论】:
【参考方案7】:使用after_init 包来处理这个问题
向有状态小部件添加didInitState()
生命周期方法,您可以在其中安全地访问继承的小部件。
InheritedWidget
在整个 Flutter 框架中被大量使用。许多状态管理包如ScopedModel 和Provider 也使用它。不幸的是,InheritedWidgets
无法通过State
的initState()
方法访问。
例子:
import 'package:flutter/material.dart';
import 'package:after_init/after_init.dart';
void main()
runApp(
MaterialApp(
home: Scaffold(
body: Example(),
),
),
);
class Example extends StatefulWidget
@override
_ExampleState createState() => _ExampleState();
class _ExampleState extends State<Example> with AfterInitMixin<Example>
Size size;
/// This gets called first, as usual.
@override
void initState()
super.initState();
// Your code here
/// This gets called after initState(), only once.
/// Safely access inherited widgets here.
@override
void didInitState()
// No need to call super.didInitState().
// setState() is not required because build() will automatically be called by Flutter.
size = MediaQuery.of(context).size;
/// This gets called after didInitState().
/// And anytime the widget's dependencies change, as usual.
@override
void didChangeDependencies()
super.didChangeDependencies();
// Your code here
/// Finally this gets called, as usual.
@override
Widget build(BuildContext context)
return Center(
child: Text(size.toString()),
);
【讨论】:
人们在开始向他们的项目添加外部依赖项之前应该三思而后行。只为这样的调用使用一个包,这是一个非常糟糕的主意!以上是关于未处理的异常:在 _ScreenState.initState() 完成之前调用了 inheritFromWidgetOfExactType(_LocalizationsScope) 或 inheri的主要内容,如果未能解决你的问题,请参考以下文章