提供者没有重建 ListView

Provider is not rebuilding ListView

我正在为我的应用程序使用提供者进行状态管理,但我 运行 遇到了问题,提供者没有在我想要结果的地方通过 ListView 重建

这是我的feed.dart

class Feed extends StatefulWidget {
  @override
  _FeedState createState() => _FeedState();
}

class _FeedState extends State<Feed> {
  @override
  void initState() {
    PostNotifier postNotifier =
        Provider.of<PostNotifier>(context, listen: false);
    getGlobalPosts(postNotifier);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    AuthNotifier authNotifier =
        Provider.of<AuthNotifier>(context, listen: false);
    PostNotifier notifier = Provider.of<PostNotifier>(context);

    return Scaffold(
      body: Padding(
        padding: EdgeInsets.only(left: 10, right: 10, top: 80),
        child: Column(
          children: <Widget>[
            Expanded(
              child: (notifier.postList.isEmpty) ? Center(child: CircularProgressIndicator(),) :
              ListView.builder(
                shrinkWrap: true,
                itemBuilder: (context, index) {
                  return PostTile(
                    userName: notifier.postList[index].userName,
                    userDp: notifier.postList[index].userDp,
                    imgSrc: notifier.postList[index].imageUrl,
                  );
                },
                physics: ScrollPhysics(),
                itemCount: notifier.postList.length,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class PostTile extends StatelessWidget {
  final String imgSrc;
  final String userName;
  final String userDp;

  PostTile(
      {@required this.userName, @required this.userDp, @required this.imgSrc});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Padding(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: Row(
            children: <Widget>[
              CircleAvatar(
                  backgroundImage: NetworkImage(
                      "https://cdn0.iconfinder.com/data/icons/users-android-l-lollipop-icon-pack/24/user-128.png")
                  ),
              FlatButton(
                child: Text(userName),
              ),
              Expanded(
                child: Container(),
              ),
              RaisedButton(
                child: Text(
                  'Follow',
                  style: TextStyle(color: Colors.white),
                ),
                color: Colors.blue,
                onPressed: () {},
              )
            ],
          ),
        ),
        SizedBox(
          height: 20,
        ),
        Image.network(imgSrc),
        SizedBox(
          height: 20,
        ),
        Padding(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              LikeButton(),
              LikeButton(
                likeBuilder: (bool isLiked) {
                  return Icon(
                    Icons.bookmark,
                    color: isLiked ? Colors.deepPurpleAccent : Colors.grey,
                    size: 30,
                  );
                },
              )
            ],
          ),
        )
      ],
    );
  }
}

和我的 getGlobalPosts 函数 - 我从 firebase 获取帖子和用户信息

getGlobalPosts(PostNotifier postNotifier) async {
  QuerySnapshot snapshot = await Firestore.instance.collection('Posts').getDocuments();

  FirebaseUser firebaseUser = await FirebaseAuth
      .instance.currentUser()
      .catchError((e) => print(e));

  List<Post> _postList = [];
  
  snapshot.documents.forEach((document) async {
    if (firebaseUser.email != document.data["email"]) {
      Post post = Post.fromMap(document.data);
      //TODO: Use this to get user
      await post.user.get().then((value) {
        post.userName = value.data['displayName'];
        post.userDp = value.data['profilePicture'];
        print(post.userDp);
      }).whenComplete(() {
        _postList.add(post);
//        print(_postList[0].userName);
        print('Success');
      });


    } else {
      print('Failed');
    }
  });

  postNotifier.postList = _postList;
}

PostNotifier -

class PostNotifier with ChangeNotifier {
  List<Post> _postList = [];
  Post _currentPost;

  List<Post> get postList => _postList;

  Post get currentPost => _currentPost;

  set postList(List<Post> postList) {
    _postList = postList;
    notifyListeners();
  }

  set currentPost(Post post) {
    _currentPost = post;
    notifyListeners();
  }
}

我正在接收数据,但我的列表视图直到我热重载才显示,只显示 CircularProgress 指示器

initState() 中删除以下代码,因为它也在构建小部件树中重新加载,似乎是多重声明问题。

PostNotifier postNotifier =
        Provider.of<PostNotifier>(context, listen: false);

通过阅读 Provider 文档

A typical situation where this happens is when starting an http request, where the future is stored inside the notifier:

initState() {
  super.initState();
  context.read<MyNotifier>().fetchSomething();
}

This is not allowed, because the modification is immediate.

Which means that some widgets may build before the mutation, while other widgets will build after the mutation. This could cause inconsistencies in your UI and is therefore not allowed.

也许 Future 在调用 build 方法之前完成了一点,所以建议(不是最佳实践,但它有效)是使用微任务在框架结束时完成 Future

Future.microtask(() => getGlobalPosts(postNotifier););

更新

尝试使用 Future.forEach 而不是仅使用 forEach,使用 Iterable.forEach 并不能保证它会等待直到 forEach 内部操作结束(在 forEach 中你使用 async/await 来执行一个future 但在 forEach 方法之外不知道它是一个 future 并且你不能使用 await snapshot.documents.forEach(...) 因为该方法的类型是 void)

getGlobalPosts(PostNotifier postNotifier) async {
  QuerySnapshot snapshot = await Firestore.instance.collection('Posts').getDocuments();

  FirebaseUser firebaseUser = await FirebaseAuth
      .instance.currentUser()
      .catchError((e) => print(e));

  List<Post> _postList = [];

  //now you can await to the forEach to end before moving on to the next line
  await Future.forEach(snapshot.documents, (document) async {
    if (firebaseUser.email != document.data["email"]) {
      Post post = Post.fromMap(document.data);
      var user = await post.user.get();
      post.userName = user .data['displayName'];
      post.userDp = user .data['profilePicture'];
      print(post.userDp);
      _postList.add(post);
      print('Success');
    } else print('Failed')
  });

  //all of the iterations of the forEach should have ended by now and _postList should have all the posts added
  postNotifier.postList = _postList;
}