未处理的异常:在 _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 中,它位于initStateBuild 之间,这样我们就可以确定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. 

使用SchedulerBindingmixin:

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 无法通过StateinitState() 方法访问。

例子:

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的主要内容,如果未能解决你的问题,请参考以下文章

第25章 SEH结构化异常处理_未处理异常及向量化异常

PyQt5 中未处理的异常

Android_程序未处理异常的捕获与处理

当 python 脚本有未处理的异常时退出代码

`未处理的异常:无效的参数:'_$_Category'的实例`在将数据发送到firestore时形成一个冻结的生成类

c++ 未处理的异常 - 如何调试