【问题标题】:StreamBuilder being called numerous times when in buildStreamBuilder 在构建时被多次调用
【发布时间】:2020-10-18 11:03:33
【问题描述】:

我有一个导航视图上有多个索引的应用程序,当我第一次加载应用程序时,正如预期的那样,StreamBuilder 被调用了两次。但是,当我导航到另一个索引并使用流构建器返回构建时,似乎正在调用流构建器以重新下载其中的所有项目。如果此循环运行时间过长,我担心这会消耗我所有的 firebase 数据。切换页面时如何重组以阻止 StreamBuilder 被多次调用。请注意,我什至会在切换并返回时保存此索引的状态。

我的构建方法:

@override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: Firestore.instance
            .collection("posts/player/post")
            .orderBy("time", descending: true)
            .snapshots()
            .take(2),
        builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
          if (!snapshot.hasData) return const Text('Loading...');
          final int highLightCount = snapshot.data.documents.length;
          return new StreamBuilder<QuerySnapshot>(
              stream: Firestore.instance
                  .collection("posts/player/post")
                  .snapshots(),
              builder: (BuildContext context,
                  AsyncSnapshot<QuerySnapshot> snapshot) {
                if (!snapshot.hasData)
                  return new Text("There are no current posts");
                return new ListView(
                  physics: const AlwaysScrollableScrollPhysics(),
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  children: getPostItems(snapshot),
                );
              });
        });
  }

当我切换它时,它正在调用几乎不间断地调用的 getPostItem(snaphot),我如何防止它被调用并消耗我的数据,或者,除非实际更新,否则防止 StreamBuilder 发生变化?

其余重要代码:

getPostItems(AsyncSnapshot<QuerySnapshot> snapshot) {
    return snapshot.data.documents.map((doc) => getListItem(doc)).toList();
  }

  Widget getListItem(var doc) {
    getProfUrl(doc);
    getDownUrl(doc);

    print("item hit");

    print("user: " + doc["user"] + "- current: " + widget.auth.getUserId());
    if (doc["user"] != widget.auth.getUserId()) {
      print("will show");
      if (doc["type"] == "image") {
        return new Column(
          children: <Widget>[
            new ListTile(
                title: new Text(doc["title"]),
                subtitle: new Text(doc["description"].toString()),
                leading: new Container(
                    width: 44.0,
                    height: 44.0,
                    decoration: new BoxDecoration(
                      shape: BoxShape.circle,
                      image: new DecorationImage(
                          fit: BoxFit.fill, image: NetworkImage(profUrl)),
                    ))),
            new Padding(
              padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
              child: new Center(
                child: new AspectRatio(
                  aspectRatio: 1 / 1,
                  child: new Container(
                    decoration: new BoxDecoration(
                        image: new DecorationImage(
                      fit: BoxFit.fill,
                      alignment: FractionalOffset.topCenter,
                      image: new NetworkImage(downUrl),
                    )),
                  ),
                ),
              ),
            ),
          ],
        );
      } else {
        VideoPlayerController _controller = VideoPlayerController.network(
            'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4')
          ..initialize().then((_) {
            // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
            setState(() {});
          });

        return new Column(children: <Widget>[
          new ListTile(
              title: new Text(doc["title"]),
              subtitle: new Text(doc["description"].toString()),
              leading: new Container(
                  width: 44.0,
                  height: 44.0,
                  decoration: new BoxDecoration(
                    shape: BoxShape.circle,
                    image: new DecorationImage(
                        fit: BoxFit.fill, image: NetworkImage(profUrl)),
                  ))),
          new Padding(
            padding: EdgeInsets.fromLTRB(4, 4, 4, 4),
            child: new Center(
              child: new AspectRatio(
                aspectRatio: 500 / 500,
                child: VideoPlayer(_controller),
              ),
            ),
          ),
        ]);
      }
    }
  }

【问题讨论】:

    标签: firebase loops flutter dart stream-builder


    【解决方案1】:

    为了减少创建Stream 的次数,您可以将其初始化移到State 类的initState 方法中。

    class MyPage extends StatefulWidget {
      @override
      _MyPageState createState() => _MyPageState();
    }
    
    class _MyPageState extends State<MyPage> {
      Stream<QuerySnapshot> _outerStream;
      Stream<QuerySnapshot> _innerStream;
    
      @override
      Widget build(BuildContext context) {
        return StreamBuilder<QuerySnapshot>(
          stream: _outerStream,
          builder: (context, snapshot) {
            if (!snapshot.hasData) return const Text('Loading...');
            final int highLightCount = snapshot.data.documents.length;
            return StreamBuilder<QuerySnapshot>(
              stream: _innerStream,
              builder: (context, snapshot) {
                if (!snapshot.hasData) return Text('There are no current posts');
                return ListView(
                  physics: const AlwaysScrollableScrollPhysics(),
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  children: getPostItems(snapshot),
                );
              },
            );
          },
        );
      }
    
      @override
      void initState() {
        super.initState();
    
        _outerStream = Firestore
            .instance
            .collection('posts/player/post')
            .orderBy('time', descending: true)
            .snapshots()
            .take(2);
    
        _innerStream = Firestore
            .instance
            .collection('posts/player/post')
            .snapshots();
      }
    }
    

    或者,如果您想在应用程序的生命周期内创建一次Stream,那么您可以使用StreamProvider 来提供Stream

    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            StreamProvider<QuerySnapshot>(
              create: (context) {
                return Firestore.instance.collection('posts/player/post').snapshots();
              },
            ),
          ],
          child: MaterialApp(
            home: MyPage(),
          ),
        );
      }
    }
    
    class MyPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final snapshot = context.watch<QuerySnapshot>();
    
        return Scaffold(
          body: snapshot == null
              ? Text('There are no current posts')
              : ListView(
                  physics: const AlwaysScrollableScrollPhysics(),
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  children: getPostItems(snapshot),
                ),
        );
      }
    }
    

    【讨论】:

    • 这是一个很棒的回应!在使用 FutureBuilder 和 StreamBuilder 时,我一直在为此苦苦挣扎,示例代码也很棒!
    • 如果你有时间,你能和我分享一下为什么你有内流和外流吗?好像第一个检查它们是否存在,第二个显示,对吗?
    • 很高兴代码示例很有用,乐于助人!我在您的代码示例中注意到您有两个不同的流。我只是将属性命名为外部和内部以突出显示它们,因此请随意将它们重命名为更有意义的名称。你是对的,外部流用于Text('Loading...')final int highLightCount,而内部流用于Text('There are no current posts')ListView(...)
    猜你喜欢
    • 1970-01-01
    • 2020-05-05
    • 2021-03-05
    • 2022-06-19
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 2019-12-25
    相关资源
    最近更新 更多