Flutter:从未来获得价值的正确方法 [英] Flutter: Correct approach to get value from Future

查看:90
本文介绍了Flutter:从未来获得价值的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个返回图像目录路径的函数,它执行一些其他检查,例如目录是否存在,然后行为相应.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆