颤振:异步等待

时间:2020-05-15 15:27:23

标签: flutter

在我的应用程序的配置文件页面中,我想使用async / await函数将将来的对象列表从Firebase集合保存到变量(myRecipes)。根据结果​​列表,我想显示不同的小部件(使用ifHasRecipes())-如果列表结果为null或为空,则我想显示一个文本小部件,否则,我想使用一个来显示列表中的对象列表视图生成器(FavoritesHomePage类)。

class Profile extends StatefulWidget {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  @override
  _ProfileState createState() => _ProfileState();
}

class _ProfileState extends State<Profile> {
  List<Recipe> myRecipes;

  Future<List<Recipe>> getUserRecipes(UserData userData) async {
    return myRecipes = await DatabaseService().findUserRecipes(userData.uid);
  }

  Widget ifHasRecipes() {
    if (myRecipes != null && myRecipes != []) {
      return FavoritesHomePage(
          recipes: myRecipes, scrollDirection: Axis.vertical, title: 'Your recipes',);
    } else {
      return Text('You have no favorites yet');
    }
  }

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);
    return StreamBuilder<UserData>(
        stream: DatabaseService(uid: user.uid).userData,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            UserData userData = snapshot.data;
            getUserRecipes(userData);
            return Scaffold(
              body: SafeArea(
                child: Column(
                  children: <Widget>[
                    //widgets using userData
                    ifHasRecipes(),
                  ],
                ),
              ),
            );
          } else {
            return Scaffold(
              body: Center(
                  child: SpinKitRipple(),),
            );
          }
        });
  }
}

如何使此代码同步?我想运行getUserRecipes(),完成后根据结果返回不同的小部件。

如果我进行热重装,代码将按我希望的方式“工作”,但是有时当我通过综合浏览器小部件导航到此个人资料页面时,返回变量myRecipes的async / await函数在如果已构建ifHasRecipes(),则myRecipes为null(即使不应该如此)...希望这不会造成混淆,对不起。

2 个答案:

答案 0 :(得分:0)

如果我正确理解了代码,解决方案是在将来解决时,通过向setState((){});方法中添加getUserRecipes()来重建窗口小部件:

Future<void> getUserRecipes(UserData userData) async {
  myRecipes = await DatabaseService().findUserRecipes(userData.uid);
  setState((){});
}

(如果您将值分配给状态,则不必返回该值,而是直接访问它。)

顺便说一句,您可以使用三元运算符(或只是常规条件)来执行条件UI。放置它而不是ifHasRecipes(),

(myRecipes != null && myRecipes != []) ?
  FavoritesHomePage(
          recipes: myRecipes, scrollDirection: Axis.vertical, title: 'Your recipes',)
  : Text('You have no favorites yet')

如果遇到此错误,请在pubspec.yaml中将最低SDK版本提高到2.6.0

答案 1 :(得分:0)

在这种情况下,您可以使用FutureBuilder,在这种情况下,您将拥有不同的状态,就像StreamBuilder,并且可以根据状态显示不同的小部件,直到Future解决。并且您有数据。

我已经对您的代码进行了一些重构,以使其可以与FutureBuilder一起使用,并且我将其更改为“无状态”,在这种情况下,它将显示CircularProgressIndicator,直到Future解决为止,它还将处理错误和数据不足。

class Profile extends StatelessWidget {
  const Profile({Key key}) : super(key: key);

  Future<List<Recipe>> getUserRecipes(UserData userData) async {
    return await DatabaseService().findUserRecipes(userData.uid);
  }

  Widget ifHasRecipes(List<Recipe> myRecipes) {
    if (myRecipes != null && myRecipes != []) {
      return FavoritesHomePage(
        recipes: myRecipes,
        scrollDirection: Axis.vertical,
        title: 'Your recipes',
      );
    } else {
      return Text('You have no favorites yet');
    }
  }

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);
    return StreamBuilder<UserData>(
      stream: DatabaseService(uid: user.uid).userData,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Scaffold(
            body: SafeArea(
              child: FutureBuilder(
                future: getUserRecipes(snapshot.data),
                builder: (context, futureSnapshot) {
                  if (futureSnapshot.hasError)
                    return Text('Error: ${futureSnapshot.error}');
                  switch (futureSnapshot.connectionState) {
                    case ConnectionState.none:
                      return Center(child: CircularProgressIndicator());
                    case ConnectionState.waiting:
                      return Center(child: CircularProgressIndicator());
                    case ConnectionState.active:
                      return Center(child: CircularProgressIndicator());
                    case ConnectionState.done:{
                      if (futureSnapshot.hasData) {
                        List<Recipe> myRecipes = futureSnapshot.data;
                        return Column(
                          children: <Widget>[
                            //widgets using userData
                            ifHasRecipes(myRecipes),
                          ],
                        );
                      }
                      return Text('There\'s no available data.');
                    }
                  }
                  return null;
                },
              ),
            ),
          );
        } else {
          return Scaffold(
            body: Center(
              child: SpinKitRipple(),
            ),
          );
        }
      },
    );
  }
}