【问题标题】:FutureBuilder doesn't wait for future to completeFutureBuilder 不等待未来完成
【发布时间】:2019-10-08 12:32:34
【问题描述】:

我正在使用 API 调用来获取数据并在 ListView 中显示。

这是未来的构建器类:

Widget futureBuilder() {
    return FutureBuilder<List<Project>>(
      future: _getProjects(),
      builder: (context, snapshot) {
        debugPrint('Builder');
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
          default:
            if (snapshot.hasError)
              return new Text('Error: ${snapshot.error}');
            else
              return listWidget(snapshot);
        }
      },
    );
  }
}

问题是,debugPrint('Builder') 甚至在 _getProjects() 返回结果之前被调用,所以快照在我的 ListView 小部件中作为 null 传递。

这里是 _getProjects() 类:

Future<List<Project>> _getProjects() async {
    List<Project> projects = [];
    String getProjects = "https://api.myjson.com/bins/1g3xpe";
    var response = await http.get(getProjects);
    Iterable list = json.decode(response.body);
    projects = list.map((model) => Project.fromJson(model)).toList();
    debugPrint('Size ' + projects.length.toString());
    return projects;
  }

这里是完整的代码:

class Projects extends StatefulWidget {
  @override
  ProjectState createState() => new ProjectState();
}

class ProjectState extends State {

   Future<List<Project>> _getProjects() async {
    List<Project> projects = [];
    String getProjects = "https://api.myjson.com/bins/1g3xpe";
    var response = await http.get(getProjects);
    Iterable list = json.decode(response.body);
    projects = list.map((model) => Project.fromJson(model)).toList();
    debugPrint('Size ' + projects.length.toString());
    return projects;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: true,
      appBar: EmptyAppBar(),
      body: Column(
        children: <Widget>[
          headerWidget(),
          futureBuilder()
        ],
      ),
    );
  }

  Widget futureBuilder() {
    return FutureBuilder<List<Project>>(
      future: _getProjects(),
      builder: (context, snapshot) {
        debugPrint('Builder');
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
          default:
            if (snapshot.hasError)
              return new Text('Error: ${snapshot.error}');
            else
              return listWidget(snapshot);
        }
      },
    );
  }
}



Widget headerWidget() {
  return Container(
    padding: EdgeInsets.all(16.0),
    color: Colors.blueAccent,
    child: Container(
        decoration: ShapeDecoration(
            color: Colors.white,
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(25.0)),
                side: BorderSide(color: Colors.white))),
        child: Row(
          children: <Widget>[
            Padding(padding: EdgeInsets.only(left: 12)),
            Icon(Icons.arrow_back, color: Colors.black54),
            Padding(padding: EdgeInsets.only(left: 12)),
            Flexible(
              fit: FlexFit.loose,
              child: searchBar(),
            ),
            Icon(
              Icons.search,
              color: Colors.black54,
            ),
            Padding(padding: EdgeInsets.only(right: 12)),
          ],
        )),
  );
}

Widget searchBar() {
  return Container(
    height: 52,
    alignment: Alignment.centerLeft,
    padding: EdgeInsets.only(left: 12, right: 12),
    child: TextField(
      decoration: new InputDecoration.collapsed(
        border: InputBorder.none,
        filled: false,
        hasFloatingPlaceholder: false,
        hintText: 'Search here',
        hintStyle: TextStyle(color: Colors.black54),
      ),
    ),
  );
}

Widget listWidget(AsyncSnapshot<List<Project>> snapshot) {
  return Scaffold(
      body: ListView.builder(
          itemBuilder: (BuildContext context, int index) {
            listItem(snapshot.data[index]);
          },
          itemCount: snapshot.data.length));
}

Widget listItem(Project project) {
  return Card(
    elevation: 6.0,
    child: Column(
      children: <Widget>[Text('Project ID'), Text('Project Name')],
    ),
  );
}

class EmptyAppBar extends StatelessWidget implements PreferredSizeWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }

  @override
  Size get preferredSize => Size(0.0, 0.0);
}

更新: 我对 FutureBuilder 做了一些改动:

Widget futureBuilder() {
    return FutureBuilder<List<Project>>(
      future: _getProjects(),
      builder: (context, snapshot) {
        debugPrint('Builder');
        switch (snapshot.connectionState) {
          case ConnectionState.done:
            if (snapshot.hasError)
              return new Text('Error: ${snapshot.error}');
            else
              return listWidget(snapshot);
            break;

          default:
            debugPrint("Snapshot " + snapshot.toString());
        }
      },
    );
  }

我现在收到此错误:

I/flutter ( 4054): _FutureBuilderState<List<Project>>#67dc4):
I/flutter ( 4054): A build function returned null.
I/flutter ( 4054): The offending widget is: FutureBuilder<List<Project>>
I/flutter ( 4054): Build functions must never return null. To return an empty space that causes the building widget to
I/flutter ( 4054): fill available room, return "new Container()". To return an empty space that takes as little room as
I/flutter ( 4054): possible, return "new Container(width: 0.0, height: 0.0)".

默认情况下 SnapShot 会返回此 Snapshot AsyncSnapshot>(ConnectionState.waiting, null, null)

【问题讨论】:

  • 在你的开关中为什么要使用状态nonewaiting
  • 仅用于调试目的。更重要的是,它们存在于我从互联网上获得的代码中。
  • 您知道switch 的工作原理吗?如果您不确定如何使用FutureBuilder,请阅读它的official documentation - 它有一些工作示例代码
  • default case 返回 null:"I/flutter ( 4054): A build function returned null."
  • 当然,欢迎您,这就是我不给鱼的原因——我给的是鱼竿;-)

标签: android flutter flutter-futurebuilder


【解决方案1】:

I/flutter (4054):构建函数返回 null。

无论如何,您总是需要从构建器返回一个小部件:

Widget futureBuilder() {
        return FutureBuilder<List<Project>>(
          future: _getProjects(),
          builder: (context, snapshot) {
            debugPrint('Builder');
            switch (snapshot.connectionState) {
              case ConnectionState.done:
                if (snapshot.hasError)
                  return new Text('Error: ${snapshot.error}');
                else
                  return listWidget(snapshot);
                break;

              default:
                debugPrint("Snapshot " + snapshot.toString());
              return Container() // also check your listWidget(snapshot) as it may return null.
            }
          },
        );
      }

问题是,debugPrint('Builder') 甚至在之前就被调用了 _getProjects() 返回结果,因此快照在我的 ListView 小部件中作为 null 传递。

这是正常的,例如,如果你有一个很长的任务(一个请求或一些非常长的计算),你将需要显示一个加载,直到那个未来“完成”。另外,将要执行的东西放在“未来”,意味着你可以让它“稍后”执行,否则你需要使用await

【讨论】:

    【解决方案2】:

    您需要检查AsyncSnapshot(快照)是否返回任何数据。 像这样添加ifstatement:

    if(snapshot.hasData && !snapshot.hasError) {
    //rest of your code
    }
    else {
    //show progress indicator or error...
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-03
      • 2015-01-07
      • 2019-04-20
      • 2014-11-11
      • 2020-10-11
      • 2020-08-05
      • 2021-08-04
      • 2020-10-04
      • 1970-01-01
      相关资源
      最近更新 更多