StreamBuilder限制

时间:2019-02-27 17:29:48

标签: dart flutter future stream-builder flutter-navigation

StreamBuilder会在收到新事件时进行重建。例如,这会导致导航(Navigator.push)问题,因为如果在导航时收到新事件,则会重新生成该触发器。由于尝试在仍在构建小部件树的同时进行导航,因此将抛出error

not possible to prevent rebuild可以根据需要避免此问题。

建议的解决方法基本上是从cache开始的。 也: herehere

但是,这还意味着StreamBuilder构建列表无法持续更新,如果还想提供列表中卡的导航功能。例如,在卡片onPressed()中。 See here

因此要刷新数据,必须使用拉动刷新...

有人有更好的解决方案吗? 还是Flutter团队正在努力解决此限制,例如通过在用户点击卡片时允许阻止重建来实现?

更新:

TL; DR是因为必须缓存StreamBuilder中的流以防止每次接收到新事件时重新生成数据,所以拉动刷新仅是更新数据的唯一方法吗?

更新2:

我已经尝试实现缓存数据,但是我的代码不起作用:

Stream<QuerySnapshot> infoSnapshot;

fetchSnapshot()  {
  Stream<QuerySnapshot> infoSnapshot = Firestore.instance.collection(‘info’).where(‘available’, isEqualTo: true).snapshots();
  return infoSnapshot;
}


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

...

child: StreamBuilder(
stream: infoSnapshot,
builder: (context, snapshot) {

if(snapshot.hasData) {
   return ListView.builder(
        itemBuilder: (context, index) =>
            build(context, snapshot.data.documents[index]),
        itemCount: snapshot.data.documents.length,
     );
  } else {
      return _emptyStateWidget();
  }

更新3:

我尝试使用StreamController,但无法实现正确的方法:

Stream<QuerySnapshot> infoStream;
StreamController<QuerySnapshot> infoStreamController = StreamController<QuerySnapshot>();

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

  infoStream = Firestore.instance.collection(‘info’).where(‘available’, isEqualTo: true).snapshots();
  infoStreamController.addStream(infoStream);
  }

...

child: StreamBuilder(
stream: infoStreamController.stream,
builder: (context, snapshot) {

更新4:

使用_localStreamController的建议给出了错误:

StreamController<QuerySnapshot> _localStreamController = StreamController<QuerySnapshot>();

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

Firestore.instance.collection(‘info’).snapshots().listen((QuerySnapshot querySnapshot) {

//      if(userAdded == null) {
        _localStreamController.add(querySnapshot);
//      }

    });
...
child: StreamBuilder(
stream: _localStreamController.stream,
builder: (context, snapshot) {
  

getter'stream'在null上被调用。

     

调用了“添加”方法   空。

2 个答案:

答案 0 :(得分:0)

基于以上评论,实际的问题似乎是在使用流导航离开视图后崩溃。您必须:

  • 当您离开时取消流控制器,以使其不再侦听其他事件。
  • 或者只是导航后不通过流发出任何新值。在其上添加一个暂停,直到您返回视图

更新:添加带有伪示例的代码

class Widget {
  // Your local stream 
  Stream<String> _localStream;
  // Value to indicate if you have navigated away
  bool hasNavigated = false;
  ...
  void init() {
    // subscribe to the firebase stream
    firebaseStream...listen((value){
      // If this value is still false then emit the same value to the localStream
      if(!hasNavigated) {
        _localStream.add(value);
      }
    });
  }

  Widget build() {
    return StreamBuilder(
      // subscribe to the local stream NOT the firebase stream
      stream: _localStream,
      // handle the same way as you were before
      builder: (context, snapshot) {
         return YourWidgets();
      }
    );
  }
}

答案 1 :(得分:0)

尝试将所有内容分解为小部件

即使您完全关闭应用程序,运行查询也应将其缓存(我相信仅在完全关闭的情况下才将其缓存长达30分钟,但是如果您仍未连接互联网,则您仍可以从 Firestore )

尝试这样的事情:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Please work')),
      body: _buildStream(context),
    );
  }

  Widget _buildStream(BuildContext context) {
     return StreamBuilder(
      stream: yourFireStoreStream,
      builder: (context, snapshot) {
        if (!snapshot.hasData) return LinearProgressIndicator();

        return _buildAnotherwidget(context, snapshot.data.documents);
      },
    );
  }
  Widget _buildAnotherwidget(Buildcontext context, List<DocumentSnapshot> snaps){
    return ListView.Builder(
      itemCount: snaps.length,
      itemBuilder:(context, index) {
      ..dostuff here...display your cards etc..or build another widget to display cards
         }
     );
 }

专注于分解成更多的小部件。最高的部分应该具有streambuilder和流。然后深入了解更多小部件。 streambuilder会自动侦听并订阅给定的流。

streambuilder更新时,它将更新下部的窗口小部件。

现在,以这种方式,当您点击下部小部件中的卡进行导航时,它不会影响最高的小部件,因为它只会影响UI。

我们将streambuilder放置在其自己的顶级小部件中...

我希望我有道理:(

我写出的代码未经测试,但我确定您可以使它正常工作