提供程序未重建ListView

时间:2020-06-27 14:29:43

标签: flutter flutter-provider

我正在使用提供程序进行应用程序的状态管理,但遇到了问题,提供程序无法通过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指示器

2 个答案:

答案 0 :(得分:2)

通过阅读提供商文档

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内部动作结束(在您使用async/await的forEach内部执行将来操作,但不能使用forEach方法不知道它是未来,并且您不能使用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;
}

答案 1 :(得分:0)

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

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