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

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

我的 initState 方法是:

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

如有任何反馈,我将不胜感激。

135488 次浏览

方法1: 您可以使用 StreamBuilder来完成此操作。这将在 溪流中的数据发生变化时运行 建筑工人方法。

下面是我的一个示例项目的代码片段:

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侦听内容的任何更改,最初它是一个空数组,并显示 循环进度指示器。一旦我进行 API 调用,获取的数据就被添加到内容数组中,该数组将运行 建筑工人方法。

当用户向下滚动时,会获取更多的内容并将其添加到内容数组中,该数组将再次运行 建筑工人方法。

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

希望这对你有帮助。

编辑:

就你的情况而言,我猜它看起来会像下面所示的那样:

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
}
});

方法2: 另一种方法是创建一个 async方法并从您的 initState()方法调用它,如下所示:

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


void asyncMethod() async {
await asyncCall1();
await asyncCall2();
// ....
}

您可以创建一个 async方法并在 initState中调用它

@override
void initState () {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
_asyncMethod();
});
        

}


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

到目前为止,使用 .then标记法似乎是可行的:

  // ...
@override
initState() {
super.initState();
myAsyncFunction
// as suggested in the comment
// .whenComplete() {
// or
.then((result) {
print("result: $result");
setState(() {});
});
}
//...

上一个答案! !

你可以设置一个布尔值,比如 load,然后在 listen 函数中将其设置为 true,当 load 设置为 true 时,让构建函数返回数据,否则只需抛出 CircularProgressIndicator

编辑 我不建议在 initState 中调用的方法中调用 setState。如果在调用 setState 时(当异步操作完成时)没有挂载小部件,则会报告错误。我建议您使用 布局之后软件包

为了更好地理解 initState 中的 setState,请查看这个答案: https://stackoverflow.com/a/53373017/9206337

这篇文章将告诉你应用程序何时完成构建方法。这样您就可以在挂载 widget 之后等待您的异步方法 setState: https://stackoverflow.com/a/51273797/9206337

示例代码:

 @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 ... ;
}

简而言之:

(() async {
await your_method();
setState(() {....anything here});
})();
@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()));
}
  @override
void initState() {
super.initState();
asyncInitState(); // async is not allowed on initState() directly
}


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

我来这里是因为我需要在程序启动时从 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;

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

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

// Create anonymous function:
() async {
await _performYourTask();
setState(() {
// Update your UI with the desired changes.
});
} ();
}

initState()build不能是异步的; 但是在这些函数中,您可以调用一个异步的函数,而不必等待该函数。

您可以创建一个 异步方法并在 InitState中调用它

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


asyncMethod(); ///initiate your method here
}


Future<void> asyncMethod async{
 

await ///write your method body here
}

由于加载或等待初始状态(通常)是一个关闭事件 FutureBuilder 似乎是一个不错的选择,因为它在异步方法上阻塞了一次; 其中异步方法可能是加载 json config、 login 等。这上面[这里]有一叠邮件。(Flutter StreamBuilder vs FutureBuilder)

根据 https://pub.dev/packages/provider的文档

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

这个怎么样?

@override
void initState() {
//you are not allowed to add async modifier to initState
Future.delayed(Duration.zero,() async {
//your async 'await' codes goes here
});
super.initState();
}

我在 initState 中使用了 timer

Timer timer;


@override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 1), (Timer timer) async {
await this.getUserVerificationInfo();
});
}


@override
void dispose() {
super.dispose();
timer.cancel();
}


getUserVerificationInfo() async {
await someAsyncFunc();
timer.cancle();
}

从初始状态移除异步

Future<void> initState() async { // this causes error, remove async
}

initState() {}//这是正确的

我强烈建议使用 FutureBuilder。它负责执行异步函数并根据结果构建小部件! 这里有一个简短的介绍视频和文档的链接。

代码示例:

  Future<void> initControllers() async {
for (var filePath in widget.videoFilePaths) {
var val = VideoPlayerController.file(File(filePath));
await val.initialize();
controllers.add(val);
}
}




@override
Widget build(BuildContext context) {
FutureBuilder(
future: initControllers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return YourWidget();
} else {
return const ProgressIndicator();
}
},
));}

尝试了所有的建议,没有人能阻止我在 initState ()完成所需的异步方法之后开始构建,除了一个: 在 State 类中有一个 bool 变量(让我们称之为 _ isDataLoade) ,它在定义时被初始化为 false,在 setState ()中被设置为 true,当异步函数在 initState ()中完成时被调用。在构建中,根据此变量的值对 CircleProcessIndicator ()或 Widget 进行条件设置。

我知道它很脏,因为它可能会破坏构建,但老实说,没有什么其他更有意义的东西——比如在完成异步函数后运行 super.initState ()——对我来说是有用的。