有没有办法在InitState方法上加载异步数据?

时间:2018-08-17 18:22:45

标签: dart flutter

我正在寻找一种在InitState方法上加载异步数据的方法,在build方法运行之前,我需要一些数据。我正在使用GoogleAuth代码,并且需要执行构建方法,直到Stream运行为止。

我的initState方法是:

 @override
  void initState () {
    super.initState();
    _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account)     {
      setState(() {
        _currentUser = account;
      });
    });
    _googleSignIn.signInSilently();
  }

感谢您的反馈。

13 个答案:

答案 0 :(得分:6)

您可以使用 StreamBuilder 来执行此操作。每当中的数据更改时,这将运行 builder 方法。

以下是我的一个示例项目中的代码段:

StreamBuilder<List<Content>> _getContentsList(BuildContext context) {
    final BlocProvider blocProvider = BlocProvider.of(context);
    int page = 1;
    return StreamBuilder<List<Content>>(
        stream: blocProvider.contentBloc.contents,
        initialData: [],
        builder: (context, snapshot) {
          if (snapshot.data.isNotEmpty) {
            return ListView.builder(itemBuilder: (context, index) {
              if (index < snapshot.data.length) {
                return ContentBox(content: snapshot.data.elementAt(index));
              } else if (index / 5 == page) {
                page++;
                blocProvider.contentBloc.index.add(index);
              }
            });
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        });
  }

在上面的代码中, StreamBuilder 侦听内容的任何变化,最初是一个空数组,并显示 CircularProgressIndicator 。调用API后,将获取的数据添加到内容数组,该数组将运行 builder 方法。

当用户向下滚动时,将提取更多内容并将其添加到内容数组,该数组将再次运行 builder 方法。

在您的情况下,仅需要初始加载。但这提供了一个选项,可以在屏幕上显示其他内容,直到获取数据为止。

希望这会有所帮助。

编辑:

在您的情况下,我猜它看起来将如下所示:

StreamBuilder<List<Content>>(
        stream: account, // stream data to listen for change
        builder: (context, snapshot) {
            if(account != null) {
                return _googleSignIn.signInSilently();
            } else {
                // show loader or animation
            }
        });

答案 1 :(得分:5)

您可以创建一个async方法,并在您的initState内调用

   @override
    void initState () {
      super.initState();
     _asyncMethod();
    }

    _asyncMethod() async {
     _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account)     {
        setState(() {
          _currentUser = account;
        });
      });
      _googleSignIn.signInSilently();
    }

答案 2 :(得分:3)

截至目前,使用.then表示法似乎有效:

  // ...
  @override
  initState() {
    super.initState();
    asyncFunction.then((result) {
    print("result: $result");
    setState(() {});
    });
  }
  //...

答案 3 :(得分:3)

  @override
  void initState() {
    super.initState();
    asyncInitState(); // async is not allowed on initState() directly
  }

  void asyncInitState() async {
    await yourAsyncCalls();
  }

答案 4 :(得分:1)

您可以设置一个类似load的布尔值,并在您的监听函数中将其设置为true,并让build函数在load设置为true时返回您的数据,否则只需抛出CircularProgressIndicator

答案 5 :(得分:1)

由于加载或等待初始状态是一个(通常)独立事件 FutureBuilder 似乎是一个不错的选择,因为它在异步方法上阻塞一次;其中异步方法可能是加载 json 配置、登录等。在堆栈中 [here] 上有一个帖子。(Flutter StreamBuilder vs FutureBuilder)

答案 6 :(得分:1)

每个文档位于 https://pub.dev/packages/provider

initState() {
  super.initState();
  Future.microtask(() =>
    context.read<MyNotifier>(context).fetchSomething(someValue);
  );
}

答案 7 :(得分:0)

示例代码:

 @override
  void initState() {
    super.initState();

    asyncOperation().then((val) {
      setState(() {});
      print("success");
    }).catchError((error, stackTrace) {
      print("outer: $error");
    });

//or

    asyncOperation().whenComplete(() {
      setState(() {});
      print("success");
    }).catchError((error, stackTrace) {
      print("outer: $error");
    });
  }

  Future<void> asyncOperation() async {
    await ... ;
  }

答案 8 :(得分:0)

甜蜜和简短:

(() async { await your_method(); setState(() {....anything here}); })();

答案 9 :(得分:0)

@override
  void initState() {
    super.initState();
     _userStorage.getCurrentUser().then((user) {
      setState(() {
        if (user.isAuthenticated) {
          Timer.run(() {
            redirectTo();
          });
        }
      });
    });
  }

 void redirectTo() {
    Navigator.push(context,
        MaterialPageRoute(builder: (BuildContext context) => new ShopOrders()));
  }

答案 10 :(得分:0)

initState()build 不能是异步的,但在这些中,您可以调用异步函数而无需等待该函数

答案 11 :(得分:-1)

我来到这里是因为我需要在程序启动时从FTP提取一些文件。我的项目是一个颤抖的桌面应用程序。主线程下载最后添加到FTP服务器的文件,解密并显示加密的内容,此方法从initState()中调用。我想在GUI出现后在后台下载所有其他文件。

上述方法均无效。构建隔离区比较复杂。

最简单的方法是使用“计算”​​方法:

  1. 将方法从FTP下载所有文件的方法移出类。
  2. 将其设为带有int参数的int函数(我不使用int参数或结果)
  3. 通过initState()方法调用它

通过这种方式,GUI会显示出来,程序会在后台下载文件。

  void initState() {
    super.initState();
    _retrieveFileList(); // this gets the first file and displays it
    compute(_backgroundDownloader, 0); // this gets all the other files so that they are available in the local directory
  }

int _backgroundDownloader(int value) {
  var i = 0;
  new Directory('data').createSync();
  FTPClient ftpClient = FTPClient('www.guckguck.de',
      user: 'maxmusterman', pass: 'maxmusterpasswort');
  try {
    ftpClient.connect();
    var directoryContent = ftpClient.listDirectoryContent();
    // .. here, fileNames list is reconstructed from the directoryContent

    for (i = 0; i < fileNames.length; i++) {
      var dirName = "";
      if (Platform.isLinux)
        dirName = 'data/';
      else
        dirName = r'data\';
      var filePath = dirName + fileNames[i];
      var myDataFile = new File(filePath);
      if (!myDataFile.existsSync())
        ftpClient.downloadFile(fileNames[i], File(filePath));
    }
  } catch (err) {
    throw (err);
  } finally {
    ftpClient.disconnect();
  }
  return i;

答案 12 :(得分:-1)

visdat中创建匿名函数,如下所示:

expandIcon