提供程序未重建ListView [英] Provider is not rebuilding ListView

查看:78
本文介绍了提供程序未重建ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用提供程序进行应用程序的状态管理,但遇到了问题,提供程序无法通过ListView在需要结果的位置进行重建

I'm using provider for state management for my app, and I'm running into a problem, provider doesn't rebuild by ListView where I want the results

这是我的feed.dart

Here is my 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和用户信息中获取帖子

and my getGlobalPosts function - I get my posts from firebase and the user info too

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-

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指示器

I'm receiving the data but my listview doesn't show up until I hot reload, Only CircularProgress indicator is shown

推荐答案

通过阅读提供商文档

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.

Perhaps the Future completes a bit before the build method is called, so the recomendation (not the best practice, but it works) is to use a microtask to complete the future at the end of the frame

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

更新

尝试使用 Future.forEach 使用Iterable.forEach不能保证它一直等到forEach内部动作结束(在forEach内部使用 async/await 来执行future,但是在forEach方法外部却不能保证它等待)知道这是未来,您不能使用 await snapshot.documents.forEach(...),因为该方法的类型为void)

Try using Future.forEach instead of only forEach, using Iterable.forEach doesn't guarantee that it awaits until the forEach inner actions end (inside the forEach you use async/await to perform a future but outside the forEach method doesn't know it is a future and you cannot use await snapshot.documents.forEach(...) because the method is of type 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;
}

这篇关于提供程序未重建ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆