【问题标题】:Provider is not rebuilding ListView提供者没有重建 ListView
【发布时间】:2020-10-18 00:48:55
【问题描述】:

我正在使用提供程序对我的应用进行状态管理,但我遇到了一个问题,提供程序不会在我想要结果的地方由 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 指示器

【问题讨论】:

  • 您能否将使用 Feed 小部件的主要小部件和其他小部件代码添加到问题中?

标签: flutter flutter-provider


【解决方案1】:

通过阅读提供者文档

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.microtask(() => getGlobalPosts(postNotifier););

更新

尝试使用Future.forEach 而不是只使用forEach,使用Iterable.forEach 并不能保证它会一直等待到forEach 内部操作结束(在forEach 内部,您使用async/await 来执行未来,但在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;
}

【讨论】:

  • 我试过 Future.microtask(() => getGlobalPosts(postNotifier));但它不工作
  • 也许问题在于 forEach 是异步的,但您应该使用方法 Future.forEach 以便循环在调用 postNotifier.postList = _postList; 之前结束(当时是空的,因为 forEach 中的期货尚未结束)并且在进行热重载时期货已经完成并且 postNotifier.postList 具有 _postList 的引用,现在它不是空的,但这只是一个理论跨度>
  • 是的 postNotifier.postList = _postList 在我完成复制电子邮件和显示名称之前被调用,我怎样才能正确?我不明白 Future.forEach
  • Future.forEach 接收一个 iterable 作为第一个参数,然后在第二个参数中对所有这些参数执行未来操作(就像常规 forEach 一样),尝试更新的代码以查看是否可以解决问题
【解决方案2】:

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

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

【讨论】:

  • 但我需要将通知程序传递给函数
  • 使用这个 PostNotifier 通知器 = Provider.of(context);并删除我在我的回答中提到的
  • 我试过了,但它给了我一个错误,我在 initState 中没有上下文
  • 只需从 initState() 中删除代码,您不会在任何地方使用它
  • 好吧,我只想在页面构建的任何时候调用该函数,就是这样
猜你喜欢
  • 2017-08-13
  • 2017-03-08
  • 1970-01-01
  • 2021-05-06
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多