简体   繁体   English

Flutter Streambuilder 不断重建

[英]Flutter Streambuilder rebuilds constantly

I have a stream builder listening to Firestore, and it kinda works.我有一个流构建器在听 Firestore,它有点工作。 The main issue is that the streambuilder keeps rebuilding (if it doesn't cost me extra reads then no worries, but if it does then it's a problem which I'm assuming it does).主要问题是流构建器不断重建(如果它不花费我额外的读取,那么不用担心,但如果它这样做了,那么我假设它是一个问题)。 When the main page first builds, when I segue to another page it builds again, and when I pop back to the main page it rebuilds for a third time.当主页第一次构建时,当我转到另一个页面时,它会再次构建,当我弹回主页时,它会第三次重建。

Code:代码:

class _RequestsListState extends State<RequestsList> with AutomaticKeepAliveClientMixin {
  final Map<String, List<Post>> posts = {};
  final List<String> postsUserIDs = [];

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);

    print('RequestsList');
    User cUser = InheritedUser.of(context).user;

    return StreamBuilder<List<Post>>(
        initialData: cUser.posts,
        stream: APIs().posts.auPostsStream(cUserID: cUser.userID),
        builder: (context, snap) {
          if (snap.hasError) {
            print('AUPostsList.dart StreamBuilder Error: ${snap.error}');
            return null;
          } else {

            print('POSTS LENGTH');
            print(snap.connectionState);
            print(cUser.posts.length);

            this.posts.clear();
            this.postsUserIDs.clear();

            snap.data.forEach((post) {
              if (this.posts[post.user.documentID] == null) {
                this.posts[post.user.documentID] = [post];
              } else {
                this.posts[post.user.documentID].add(post);
              }

              if (!this.postsUserIDs.contains(post.user.documentID)) {
                this.postsUserIDs.add(post.user.documentID);
              }
            });

            return ListView.separated(
                controller: widget.scrollController,
                physics: BouncingScrollPhysics(),
                shrinkWrap: true,
                padding: EdgeInsets.only(top: 0.0),
                itemBuilder: (context, index) => RequestItem(posts: this.posts[this.postsUserIDs[index]]),
                separatorBuilder: (context, index) => Container(),
                itemCount: this.postsUserIDs.length);
          }
        });
  }
}

And secondly, the when I segue to the second page, the array gets emptied.其次,当我转到第二页时,数组被清空。 I'm not clearing it anywhere so I'm not sure why it empties the array after tapping an item...我没有在任何地方清除它,所以我不确定为什么它在点击一个项目后清空数组......

Log:日志:

Reloaded 0 of 773 
libraries in 169ms.
flutter: ChatsList
flutter: RequestsList
flutter: POSTS LENGTH
flutter: ConnectionState.waiting
flutter: 1
flutter: RequestsList
flutter: POSTS LENGTH
flutter: ConnectionState.waiting
flutter: 0
flutter: ChatsList
flutter: RequestsList
flutter: POSTS LENGTH
flutter: ConnectionState.waiting
flutter: 0
flutter: ChatsList

StreamBuilder gets fired every time there is change in streams or build method gets called. 每当流中发生任何更改或调用build方法时,都会触发StreamBuilder。 So to overcome this issue in your case change your code as below. 因此,要解决此问题,请按如下所示更改代码。

class _RequestsListState extends State<RequestsList> with AutomaticKeepAliveClientMixin {
  final Map<String, List<Post>> posts = {};
  final List<String> postsUserIDs = [];
  StreamController<QuerySnapshot> _postStreamController = StreamController<QuerySnapshot>();

  @override
  void initState() {
  super.initState();

  _buildPostStreams();
  }

  _buildPostStreams() async{
     _postStreamController.add(APIs().posts.auPostsStream(cUserID: cUser.userID));
  }

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);

    print('RequestsList');
    User cUser = InheritedUser.of(context).user;

    return StreamBuilder<List<Post>>(
        initialData: cUser.posts,
        stream: _postStreamController.stream,
        builder: (context, snap) {
          if (snap.hasError) {
            print('AUPostsList.dart StreamBuilder Error: ${snap.error}');
            return null;
          } else {

            print('POSTS LENGTH');
            print(snap.connectionState);
            print(cUser.posts.length);

            this.posts.clear();
            this.postsUserIDs.clear();

            snap.data.forEach((post) {
              if (this.posts[post.user.documentID] == null) {
                this.posts[post.user.documentID] = [post];
              } else {
                this.posts[post.user.documentID].add(post);
              }

              if (!this.postsUserIDs.contains(post.user.documentID)) {
                this.postsUserIDs.add(post.user.documentID);
              }
            });

            return ListView.separated(
                controller: widget.scrollController,
                physics: BouncingScrollPhysics(),
                shrinkWrap: true,
                padding: EdgeInsets.only(top: 0.0),
                itemBuilder: (context, index) => RequestItem(posts: this.posts[this.postsUserIDs[index]]),
                separatorBuilder: (context, index) => Container(),
                itemCount: this.postsUserIDs.length);
          }
        });
  }
}

I hope this will help you. 我希望这能帮到您。

I know this is late but moving the stream outside of the build context either using your init method, or using provider to do the call in a services file is how I do my setups.我知道这已经很晚了,但是使用您的 init 方法或使用提供程序在服务文件中进行调用将流移到构建上下文之外是我进行设置的方式。 Let me know if you came right.如果你来对了,请告诉我。

Your assumption is correct... because the StreamBuilder is in your Build method it will read from Firestore each time the Widget is instantiated.您的假设是正确的...因为 StreamBuilder 在您的 Build 方法中,每次实例化小部件时它都会从 Firestore 读取。

So in this case using AutomaticKeepAliveClientMixin to keep the Widget state alive doesn't help.所以在这种情况下,使用AutomaticKeepAliveClientMixin来保持 Widget 状态并没有帮助。

Wesley is right - you should use the init method to create your stream - this way the stream is only called once. Wesley 是对的——你应该使用 init 方法来创建你的流——这样流只被调用一次。 Check out this answer for more info查看此答案以获取更多信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM