【问题标题】:How to safely call setState function from async function in flutter?如何在颤动中从异步函数安全地调用 setState 函数?
【发布时间】:2020-03-19 17:43:42
【问题描述】:

我的 Flutter 应用程序中有一个下载按钮,在下载文件时会在按下按钮之前显示 Text("download") CircularProgressIndicator(),在下载文件时会显示 Text("Open")

_FileStatus status = _FileStatus.EMPTY;

RaisedButton raisedButton() {
  var child;
  if (status == _FileStatus.EMPTY) {
    child = Text("download");
  } else if (status == _FileStatus.DOWNLOADING) {
    child = CircularProgressIndicator();
  } else {
    child = Text("Open");
  }
  return RaisedButton(
    child: child,
    onPressed: () {
      if (status == _FileStatus.EMPTY) {
        setState(() {
          status = _FileStatus.DOWNLOADING;
        });
        download().then((bool isDownloaded) {
          if (isDownloaded) {
            //setState status=>Downloaded
          } else {
            //setState status=>Empty
          }
        });
      } else {
        print("Open");
      }
    },
  );
}

Future<bool> download() async {
  var response = await Future<bool>.delayed(Duration(seconds: 5), () {
    print("Downloading ...");
    return true;
  });
  if (response) {
    print("Downloaded");
    return true;
  } else {
    print("Download Failed!");
    return false;
  }
}

但是这个按钮位于应用程序的第二页。因此,人们可能会按下下载按钮,然后返回第一页。通常,如果我按下下载按钮然后等待它完成,那么 setState 会将status 更新为DOWNLOADED;没问题。但是如果我按下download 按钮然后返回,那么download() 将在后台运行,当它完成时它会尝试运行我已经注释掉的setState 方法之一。这要么给出错误,要么说内存泄漏问题。

我已经尝试通过使用 isLoaded 变量来解决这个问题。

@override
void initState() {
  isLoaded = true;
  super.initState();
}
@override
void dispose() {
  isLoaded = false;
  super.dispose();
}

然后在调用 setState 方法之前检查 isLoaded 是否为真。

if (isLoaded) {
  setState(() {
    status = _FileStatus.DOWNLOADING;
  });
}

但这感觉像是一种 hacky 方法,我知道必须有更好的方法来解决这个问题。我搜索了其他类似的问题,但找不到我要找的东西。

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    只需添加一个条件来检查您的状态是否已挂载,如果不是 - 不要调用setState

              download().then((bool isDownloaded) {
                // Checking if widget is mounted
                if (!mounted) {
                  return;
                }
    
                ...
    

    【讨论】:

    • 这实际上解决了我最初的问题。但我有新问题。在我再次加载第二页后,setState 这次没有按预期运行,也没有更新状态。所以用户在看到下载时被卡住了......解决方案是什么?有什么想法吗?
    • @KshitijDhakal 如果没有看到代码,我无法理解您的问题。 “再次加载第二页”是什么意思?
    • 原来我需要调用 setState 来更新 UI。但问题是 download() 是异步的,因此即使在屏幕已经从导航器中弹出之后它也会执行。现在(这不是真正的问题)我希望能够在屏幕弹出一次然后再次推回后,从 download() 对 setState 的最后一次调用更新 UI。 TLDR;卸载并再次安装后,从页面中的异步函数调用 setState。我真的很想在这方面提供帮助。如果我不够清楚,我们可以在聊天中继续。我可以提供一些代码。
    • @KshitijDhakal 但是当你推送一条路由时,它应该为它创建一个新状态,而不是使用旧状态,不是吗?因为旧的在弹出后会被丢弃。我错过了什么吗?
    • 你说得对。我只是想实现下载按钮。并希望在下载完成后打开更新按钮(只要用户保持在同一屏幕上,它就可以工作)。但正如你所解释的,也许我应该改变处理这个问题的方式。无论如何,谢谢。
    猜你喜欢
    • 2022-07-11
    • 2020-09-18
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多