Flutter:从未来获得价值的正确方法 [英] Flutter: Correct approach to get value from Future
问题描述
我有一个返回图像目录路径的函数,它执行一些其他检查,例如目录是否存在,然后行为相应.
I have a function which returns images directory path, it performs some additional check like if directory exists or not, then it behaves accordingly.
这是我的代码:
Future<String> getImagesPath() async {
final Directory appDir = await getApplicationDocumentsDirectory();
final String appDirPath = appDir.path;
final String imgPath = appDirPath + '/data/images';
final imgDir = new Directory(imgPath);
bool dirExists = await imgDir.exists();
if (!dirExists) {
await new Directory(imgPath).create(recursive: true);
}
return imgPath;
}
这段代码可以按预期工作,但是我在从Future
获取价值时遇到了问题.
This piece of code works as expected, but I'm having issue in getting value from Future
.
案例方案:
我将数据存储在本地数据库中,并试图在listview中显示它.我正在使用FutureBuilder
,如本中所述答案.每个数据行都有一个与之相连的映像(连接表示映像名称存储在db中).
Case Scenario:
I have data stored in local database and trying to display it, inside listview. I'm using FutureBuilder
, as explained in this answer. Each data row has an image connected with it (connected means, the image name is stored in db).
在Widget build
方法内部,我有以下代码:
Inside Widget build
method, I have this code:
@override
Widget build(BuildContext context) {
getImagesPath().then((path){
imagesPath = path;
print(imagesPath); //prints correct path
});
print(imagesPath); //prints null
return Scaffold(
//removed
body: FutureBuilder<List>(
future: databaseHelper.getList(),
initialData: List(),
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, int position) {
final item = snapshot.data[position];
final image = "$imagesPath/${item.row[0]}.jpg";
return Card(
child: ListTile(
leading: Image.asset(image),
title: Text(item.row[1]),
subtitle: Text(item.row[2]),
trailing: Icon(Icons.launch),
));
})
: Center(
child: CircularProgressIndicator(),
);
}));
}
在.then
内移动return Scaffold(.....)
无效.因为小部件构建不返回任何内容.
Shifting return Scaffold(.....)
inside .then
doesn't work. Because widget build returns nothing.
我找到的另一个选项是async/await
,但是最后,同样的问题,下面的代码可用:
The other option I found is async/await
but at the end, same problem, code available below:
_getImagesPath() async {
return await imgPath();
}
调用_getImagesPath()
会返回Future
,而不是实际数据.
Calling _getImagesPath()
returns Future
, instead of actual data.
我相信这是一个很小的逻辑错误,但我自己却找不到.
I beleive there is very small logical mistake, but unable to find it myself.
推荐答案
我看到您必须从两个Future的输出中构建窗口小部件.您可以使用两个FutureBuilders
或使用一个辅助方法将它们组合为一个简化的代码单元.
I see that you have to build your widget from the output of two futures. You can either use two FutureBuilders
or have a helper method to combine them into one simplified code unit.
此外,切勿从build
函数计算/调用异步函数.必须先对其进行初始化(在构造函数或initState
方法中),否则该窗口小部件可能最终会永远重新绘制自身.
Also, never compute/invoke async function from build
function. It has to be initialized before (either in constructor or initState
method), otherwise the widget might end up repainting itself forever.
解决方案:为简化代码,最好将两个将来的输出合并到一个类中,如下例所示:
Coming to the solution: to simplify code, it is better to combine both future outputs into a single class as in the example below:
build
方法所需的数据:
class DataRequiredForBuild {
String imagesPath;
List items;
DataRequiredForBuild({
this.imagesPath,
this.items,
});
}
获取所有必需数据的功能:
Function to fetch all required data:
Future<DataRequiredForBuild> _fetchAllData() async {
return DataRequiredForBuild(
imagesPath: await getImagesPath(),
items: await databaseHelperGetList(),
);
}
现在将所有内容放到Widget中:
Now putting everything together in Widget:
Future<DataRequiredForBuild> _dataRequiredForBuild;
@override
void initState() {
super.initState();
// this should not be done in build method.
_dataRequiredForBuild = _fetchAllData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
//removed
body: FutureBuilder<DataRequiredForBuild>(
future: _dataRequiredForBuild,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.items.length,
itemBuilder: (_, int position) {
final item = snapshot.data.items[position];
final image = "${snapshot.data.imagesPath}/${item.row[0]}.jpg";
return Card(
child: ListTile(
leading: Image.asset(image),
title: Text(item.row[1]),
subtitle: Text(item.row[2]),
trailing: Icon(Icons.launch),
));
})
: Center(
child: CircularProgressIndicator(),
);
},
),
);
}
希望有帮助.
这篇关于Flutter:从未来获得价值的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!