在 dispose() 之后调用 setState()

Posted

技术标签:

【中文标题】在 dispose() 之后调用 setState()【英文标题】:setState() called after dispose() 【发布时间】:2018-08-26 15:56:39 【问题描述】:

当我点击凸起的按钮时,时间选择器就会出现。现在,例如,如果我等待 5 秒,然后确认时间,则会出现此错误: setState() 在 dispose() 之后调用

我确实在控制台中看到了颤振是如何更新父小部件的,但是为什么呢?我什么都不做——我只等5秒?! 下面的示例将在普通项目中工作,但是在我的项目中它更复杂,因为在我等待时 Flutter 正在更新状态......我做错了什么?有没有人猜到 Flutter 在我更复杂的项目中而不是在简单项目中随机更新的原因是什么?

[更新] 我再次查看它,发现它正在从我的TabBarTabBarView 所在的级别更新。 它是否必须与TabBarView 所需的“with TickerProviderStateMixin”有关?会不会是导致应用定期随机刷新?

 class DateTimeButton extends State<DateTimeButtonWidget> 
  DateTime selectedDate = new DateTime.now();

  Future initTimePicker() async 
    final TimeOfDay picked = await showTimePicker(
      context: context,
      initialTime: new TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute),
    );

    if (picked != null) 
      setState(() 
        selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
      );
    
  

  @override
  Widget build(BuildContext context) 
    return new RaisedButton(
      child: new Text("$selectedDate.hour $selectedDate.minute"),
      onPressed: () 
        initTimePicker();
      
    );
  

【问题讨论】:

"with TickerProviderStateMixin" - 是的,我认为这会导致您的小部件被重建。 【参考方案1】:

在调用 setState() 之前,只需检查小部件状态类的布尔属性 mounted

if (this.mounted) 
  setState(() 
    // Your state change code goes here
  );

或者更干净的方法 在 StatelfulWidget 类中覆盖 setState 方法。

class DateTimeButton extends StatefulWidget 
  @override
  void setState(fn) 
    if(mounted) 
      super.setState(fn);
    
  

【讨论】:

只是阻止设置状态,并没有解决无法设置状态的问题 @temirbek 我不确定您的用例是什么,该 if 条件的目的正是在未安装小部件后停止尝试设置状态,这意味着小部件不存在屏幕上小部件的层次结构。 好的,我的用例是屏幕 A 调用屏幕 B 并等待结果。屏幕 B 返回结果,但到那时屏幕 A 重新创建,并且正在等待结果的旧屏幕被卸载。但我需要在 ScreenA 中使用从 ScreenB 返回的值设置状态 在您的用例中,您必须确保 ScreenA 在 ScreenB 返回时具有相同的实例,检查 ScreenA 键是否相同,如果不能这样做,并且 ScreenA 必须重新创建新实例,那么你将不得不使用某种方式来实现状态管理,比如 ScopedModel、Bloc 或 FlutterRedux 等。 为什么框架默认不做内部检查?有没有可以为已处理的小部件设置状态的情况?!【参考方案2】:

如果在小部件已被处置时Future 完成是预期行为,您可以使用

if (mounted) 
  setState(() 
    selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
  );

【讨论】:

我会推荐docs.flutter.io/flutter/widgets/State/mounted.html,而不是使用您自己的会员。 谢谢伙计。这节省了我的一天(绝对是一周):D @Gunter,我们如何在 super.dispose() 之前调用 setState;在 dispose() 函数中。它对我不起作用,并收到错误消息,例如 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4263 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true。我已经在使用 if (mounted) setState(() );请提出什么问题。非常感谢。【参考方案3】:

setState()前写一行

 if (!mounted) return;

然后

setState(() 
      //Your code
    );

【讨论】:

【参考方案4】:

我遇到了同样的问题,我解决了更改initState() 上的超级构造函数调用顺序:

错误代码:

@override
  void initState() 
    foo_bar(); // in foo_bar i call setState();
    super.initState(); // state initialization after foo_bar()
  

正确的代码:

@override
  void initState() 
    super.initState();
    foo_bar(); // first call super constructor then foo_bar that contains setState() call
  

【讨论】:

这很有帮助,但我想知道为什么,我以相反的顺序将它放在另一个地方并且没有给我任何问题【参考方案5】:

为了防止错误发生,可以利用State 类的mounted 属性来确保在设置它的状态之前挂载一个小部件:

// First Update data 

if (!mounted)  
      return;
 
setState(()  

【讨论】:

写答案时,请在代码中写几句话,不要只用一个词。【参考方案6】:

试试这个

Widget build(BuildContext context) 
    return new RaisedButton(
        child: new Text("$selectedDate.hour $selectedDate.minute"),
        onPressed: () async 
            await initTimePicker();
        
    );

【讨论】:

【参考方案7】:
class MountedState<T extends StatefulWidget> extends State<T> 
  @override
  Widget build(BuildContext context) 
    return null;
  

  @override
  void setState(VoidCallback fn) 
    if (mounted) 
      super.setState(fn);
    
  

示例

为了防止错误,使用 MountedState

而不是使用 State
class ExampleStatefulWidget extends StatefulWidget 
  const ExampleStatefulWidget(Key key) : super(key: key);

  @override
  _ExampleStatefulWidgetState createState() => _ExampleStatefulWidgetState();


class _ExampleStatefulWidgetState extends MountedState<ExampleStatefulWidget> 
  ....

【讨论】:

以上是关于在 dispose() 之后调用 setState()的主要内容,如果未能解决你的问题,请参考以下文章

Flutter的setState更新原理和流程

为啥在 setState 之后调用 getDerivedStateFromProps?

在等待之后调用 setState 时,状态立即可用

即使在调用 setState 之后,React.js 子状态也不会重新渲染?

怎么可能在 API 调用和 setState 之后,同一个对象的某些元素可以访问,而其他元素不能访问?

在 Flutter 中从 main() 调用 setState()