Async await 颤振:从UI调用异步代码的最佳实践

Async await 颤振:从UI调用异步代码的最佳实践,async-await,dart,flutter,Async Await,Dart,Flutter,我正在开发一个flatter应用程序,但不知道从UI调用异步代码时应该做什么——比如说UI小部件的构建方法 例如,我的应用程序通过服务类连接Firebase,该服务类使用异步等待样式从Firebase获取记录。使用wait可以确保我的服务类的方法在返回UI端之前完成记录检索 但是,由于服务类的方法被标记为async,因此在服务类的方法完成之前,它会立即将未来返回给调用的UI小部件,并且UI代码保持运行 是的,我可以为“then()”回调编写代码来处理检索到的记录,但是接下来的小部件中的其他代码如

我正在开发一个flatter应用程序,但不知道从UI调用异步代码时应该做什么——比如说UI小部件的构建方法

例如,我的应用程序通过服务类连接Firebase,该服务类使用异步等待样式从Firebase获取记录。使用wait可以确保我的服务类的方法在返回UI端之前完成记录检索

但是,由于服务类的方法被标记为async,因此在服务类的方法完成之前,它会立即将未来返回给调用的UI小部件,并且UI代码保持运行

是的,我可以为“then()”回调编写代码来处理检索到的记录,但是接下来的小部件中的其他代码如何,取决于异步调用的结果?这是否意味着我需要将所有内容嵌入到“then()”子句中,并避免在返回未来的方法调用之后添加任何执行步骤?对于这种使用模式有什么建议吗

@override
Widget build(BuildContext context) {

   RealtimeDatabase.getData()  // my service class
      .then((onValue) {
        ..... // do something after the async call is completed
   }

   ..... // do something immediately before the async call is done


class RealtimeDatabase {

    Future<String> getData() async {

    DataSnapshot dataSnapshot = await FirebaseDatabase.instance.reference().orderByKey().once(); // will wait until completed
    .....
    .....
@覆盖
小部件构建(构建上下文){
RealtimeDatabase.getData()//我的服务类
.然后((onValue){
..…//在异步调用完成后执行某些操作
}
..…//在异步调用完成之前立即执行某些操作
类实时数据库{
Future getData()异步{
DataSnapshot DataSnapshot=等待FirebaseDatabase.instance.reference().orderByKey().once();//将等待完成
.....
.....
对不起,如果我在这里的描述不够清晰,欢迎任何建议


Jimmy

在Flatter中,有一些小部件可以帮助您无缝地执行此操作(例如,,),并且您可以根据它们的分辨率控制要渲染的内容

FutureBuilder
上的示例:

Widget build(BuildContext context) {
    return new FutureBuilder(
    future: FirebaseDatabase.instance.reference().child("node"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
          return snapshot.hasData? new Scaffold(
          ///start building your widget tree
          ):new CircularProgressIndicator(); ///load until snapshot.hasData resolves to true
},);
  }
class Database {

  DatabaseReference _refProfile = FirebaseDatabase.instance.reference().child(
      "profiles");

  getProfiles() => _refProfile.onValue; }
StreamBuilder
示例:

Widget build(BuildContext context) {
    return new FutureBuilder(
    future: FirebaseDatabase.instance.reference().child("node"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
          return snapshot.hasData? new Scaffold(
          ///start building your widget tree
          ):new CircularProgressIndicator(); ///load until snapshot.hasData resolves to true
},);
  }
class Database {

  DatabaseReference _refProfile = FirebaseDatabase.instance.reference().child(
      "profiles");

  getProfiles() => _refProfile.onValue; }

Widget build(BuildContext context) {
      return new StreamBuilder<Event>(
          stream: _database.getProfiles(), //_database = new Database()
          builder: (BuildContext context, AsyncSnapshot<Event> event) {
          return event.hasData?new Scaffold(

///build your widget tree
                    ):new CircularProgressIndicator();

/// place holder
}
小部件构建(构建上下文){
返回新的StreamBuilder(
流:_database.getProfiles(),//_database=new database()
生成器:(BuildContext上下文,异步快照事件){
返回事件.hasData?新脚手架(
///构建小部件树
):新的循环压缩机指示器();
///占位符
}
值得一提的是

FutureBuilder
在您希望一次获取某些数据,而不关心保持一致连接或跟踪数据中的任何更改时,最好使用


另一方面,
StreamBuilder
使您能够持续监听数据,并且您可以根据数据中的任何更新更新UI的状态。

虽然我认为@aziza提到的方法肯定是可行的,但值得注意的是,您也可以“自己滚动”

例如,在
initState()
中,您可以执行以下操作:

@override
void initState() {
    super.initState();
    _myItems = // some sane default
    FirebaseDatabase.instance.reference().child("node")
      .then((items) {
        if (widget.mounted) setState(() { _myItems = items; }
      });
}

Widget build(BuildContext context) {
  return new Scaffold(
   ///start building your widget tree
  );
}

最好在这里使用
FutureBuilder
,但这是另一种方法(即使这不一定是最佳实践)tion。非常感谢,我已经挣扎了几天,不知道Flitter有这么好的功能!似乎一个返回语句缺少StreamBuilder示例。我想知道的是“构建您的小部件树”的位置发生了什么注释。例如,如何在ListView中显示流配置文件生成的内容…@Wonderjimmy这是我的错,我应该更清楚,你永远不会想在脚手架级别上执行FutureBuilder,而是在body属性中执行。这样,你的appbar、drawer、FAB、tabs…等将正常加载,并且仅在正文中加载content将加载。嗨,Dan,在您的解决方案中,构建方法是否被调用两次?一次是在_myItems为null时,第二次是在调用setState之后?但是在重建期间,是否会再次调用initState?initState仅在第一次将小部件添加到树中时才会被调用。但是,是的,在本例中,构建将被调用两次,一次是在状态的初始化,在将来解析之后。initState是不可取的,因为当您回到旧屏幕时,有时我们需要调用API,为此,FutureBuilder是最好的方法。