如何完全处理我的Stateful Widget?

51

我称我的有状态小部件为“页面”,并从服务器获取一些信息。如果没有找到任何信息,它会警告用户没有任何信息。 从抽屉返回按钮,我回到上一页。如果我快速来回地重复操作,我的IntelliJ IDE控制台会显示错误消息:

E/flutter (22681): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter (22681): setState() called after dispose(): _BillsPayWaterState#66be5(lifecycle state: defunct, not mounted)
E/flutter (22681): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose of the () callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter (22681): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose of ().
E/flutter (22681): #0      State.setState.<anonymous closure> 

在我的有状态小部件中,我有@override void initState()@override void dispose()

我的问题是,当我使用抽屉式导航栏的返回按钮时,如何完全销毁我的有状态小部件?

这是小部件的调用

onTap: () {
Navigator.push(
  context,
  SlideRightRoute(widget: new BillsWaterPay(“S02")),
);
}

这是 BillsWaterPay 小部件

List<List<dynamic>> _userAskBills;

// TODO: KURUM KODU - SABIT TELEFON
String _kurumKodu = "";
String _noDataText = "";

class BillsWaterPay extends StatefulWidget {
  final String title;

  BillsWaterPay(this.title);

  @override
  _BillsWaterPayState createState() =>
      _BillsWaterPayState();
}

class _BillsWaterPayState extends State<BillsWaterPay> {

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _kurumKodu = widget.title;
    _userAskBills;
    _noDataText;
    _loadEverything();
}

Future _loadEverything() async {
    await _getDataFromServer();
}

Future _getDataFromServer() async() {
    …
    …
    if no data
        setState
            _noDataText = “No Data”;  // In here I get an error setState called after dispose

}


@override
void dispose() {
  super.dispose();
      _kurumKodu = widget.title;
    _userAskBills;
    _noDataText;
}

我们需要看到您的小部件代码。如果您注册事件处理程序,则需要逐个注销它们。没有明确的方法来处理您的小部件,您只能准备好让Dart处理它。 - Günter Zöchbauer
@Günter 我更新了我的问题。请看一下 Future _getDataFromServer() async() 这一行代码,我得到了一个错误。 - Nick
在 Github 上有关于这个问题的持续讨论,请在认为应该在 Flutter 中修复它的情况下表达您的支持。 - morgwai
3个回答

116

我认为问题是由于小部件被处理后,服务器响应才到达所导致的。

在调用setState之前,请检查小部件是否已经挂载。这应该可以防止您看到的错误:

// Before calling setState check if the state is mounted. 
if (mounted) { 
  setState (() => _noDataText = 'No Data');
}

谢谢,if(mounted)是什么意思?如果mounted为true或false,我应该如何处理?在new BillsWaterPay(“S02”)中,“S02”始终会不同,并且每个不同的参数都需要我获得不同的结果。mounted如何帮助我解决这个问题? - Nick
1
如果 mountedfalse,则不要调用 setState。这意味着该小部件不是渲染树的一部分。 - Günter Zöchbauer
谢谢。当我调用SlideRightRoute(widget: new BillsWaterPay("S02"))时,难道不是在创建一个新的小部件吗?而且void dispose()是我假设要处理旧的widget. 那么为什么,widget不是呈现树的一部分呢?我在哪里可以获得详细信息? - Nick
1
我并不是说它从未成为渲染树的一部分,只是不会在服务器响应到达时。此时,你的代码可能已经创建了一个新实例,也许是从父窗口小部件的setState操作中创建的(大多是猜测和一般信息,因为你的问题没有提供太多细节)。这就是为什么从小部件内部发出服务器请求通常是一个坏主意。至少响应应该被缓存到小部件之外,这样当请求相同的数据时,它实际上是从缓存中使用而不是再次从服务器获取。 - Günter Zöchbauer
你应该使用FutureBuilder而不是自己处理所有事情。FutureBuilder会处理所有的事情。 - Rémi Rousselet
@RémiRousselet FutureBuilder实际上是如何处理这个问题的?例如,当FutureBuilder“等待”服务器响应时,小部件或其父级仍然可以被销毁,因此在网络响应完成时不在小部件树中。然后会得到相同的错误。 - mikeyman22

13

在异步处理过程中,当我们对小部件进行处理后再调用setState()方法时,会出现上述异常。

通过使用Flutter的属性mounted来解决这个问题。

mounted 属性指示小部件是否可用。

...
if (mounted) {
    setState(() {
        ...
    });
}
...

0
检查小部件是否已安装
@override
  void setState(VoidCallback fn) {
    if (mounted) {
      super.setState(fn);
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接