Flutter:从 Future 中获取价值的正确方法
Posted
技术标签:
【中文标题】Flutter:从 Future 中获取价值的正确方法【英文标题】:Flutter: Correct approach to get value from Future 【发布时间】:2020-01-22 02:37:10 【问题描述】:我有一个返回图像目录路径的函数,它执行一些额外的检查,例如目录是否存在,然后它会相应地运行。
这是我的代码:
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
获取价值时遇到问题。
案例场景:
我将数据存储在本地数据库中并尝试在列表视图中显示它。我正在使用FutureBuilder
,正如answer 中所解释的那样。每个数据行都有一个与之相连的图像(连接的意思是,图像名称存储在db中)。
在Widget build
方法中,我有这段代码:
@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(.....)
不起作用。因为小部件构建不返回任何内容。
我找到的另一个选项是async/await
,但最后,同样的问题,代码如下:
_getImagesPath() async
return await imgPath();
调用_getImagesPath()
返回Future
,而不是实际数据。
我相信有很小的逻辑错误,但我自己找不到。
【问题讨论】:
你的问题是什么?我在你的代码中没有看到任何FutureBuilder
...所以你是在使用它还是StatefulWidget
例如?
我正在使用StatefulWidget
,我已经分享了完整的代码,如果您需要更多信息,请告诉我。
不,在您现在发布的代码中,您使用的是FutureBuilder
,所以我的问题是:它是正确的代码吗?或者你正在使用StatefulWidget
?
我看到你知道如何在代码中使用FutureBuilder
,并且你也知道将_getImagesPath
设为异步函数。将这两者结合起来有什么错误吗?
@pskink 正如我在第一条评论中提到的,我使用的是StatefulWidget
。 Widget build
方法在 StatefulWidget
类中。
【参考方案1】:
我看到您必须从两个期货的输出中构建您的小部件。您可以使用两个 FutureBuilders
或使用辅助方法将它们组合成一个简化的代码单元。
另外,永远不要从 build
函数计算/调用异步函数。它必须在之前初始化(在构造函数或initState
方法中),否则小部件可能最终会永远重新绘制自己。
解决方案:为了简化代码,最好将两个未来的输出组合到一个类中,如下例所示:
build
方法所需的数据:
class DataRequiredForBuild
String imagesPath;
List items;
DataRequiredForBuild(
this.imagesPath,
this.items,
);
获取所有必需数据的函数:
Future<DataRequiredForBuild> _fetchAllData() async
return DataRequiredForBuild(
imagesPath: await getImagesPath(),
items: await databaseHelperGetList(),
);
现在把所有东西放在 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(),
);
,
),
);
希望对你有帮助。
【讨论】:
感谢您的回答,使用您的解决方案解决了路径问题。但由于某些原因,资产仍未显示。控制台unable to load asset: complete_path_of_asset
出现错误,路径100% 正确且资产也存在。为了测试,我从调试控制台复制了一条路径并在Image.asset
中输入,它运行良好。这怎么可能?
确保路径中没有双斜杠,这可能是个问题
我已经仔细检查过,没有任何问题;包括双斜线。路径是正确的,因为我已经从控制台复制了相同的路径并在 Image.asset
内部使用,它工作正常。
我注意到一件奇怪的事情,如果我运行应用程序一次并导航到该屏幕。之后,如果我从 IDE 中单击 Restart
按钮,它会重新打开应用程序并进入登录屏幕。当我登录并在那里导航时,图像工作正常。代码没有任何变化,在重新启动期间,我尝试了几次,它可以工作。可能油漆有问题或者需要async/await
。
来自控制台<asynchronous suspension>
Image provider: AssetImage(bundle: null, name: "/data/user/0/com.alena.data_handler/app_flutter/images/1.jpg")
的错误行【参考方案2】:
将这段代码移入FutureBuilder
应该可以解决问题。
getImagesPath().then((path)
imagesPath = path;
print(imagesPath); //prints correct path
);
所以你的最终代码应该是这样的:
@override
Widget build(BuildContext context)
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)
getImagesPath().then((path)
imagesPath = path;
);
final item = snapshot.data[position];
final image = "$imagesPath/$item.row[0].jpg";
return Card(
child: ListTile(
leading: Image.file(File(image)),
title: Text(item.row[1]),
subtitle: Text(item.row[2]),
trailing: Icon(Icons.launch),
));
)
: Center(
child: CircularProgressIndicator(),
);
));
希望对你有帮助!
【讨论】:
感谢您的回答,它在第一次加载时不起作用。但是当我们回去时它会起作用,然后再回来,非常奇怪的问题。 @Atlas 坚持 OP 的结构。如果有人问如何修车,不建议买新车。以上是关于Flutter:从 Future 中获取价值的正确方法的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法从 Flutter 中的 Future 函数获取反馈
Flutter - 如何在我的未来构建器文本小部件中获得价值回报
在 Flutter 的流构建器中使用 Future 构建器是正确的