StreamBuilder 限制

Posted

技术标签:

【中文标题】StreamBuilder 限制【英文标题】:StreamBuilder limitation 【发布时间】:2019-07-21 12:23:33 【问题描述】:

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

not possible to prevent rebuild 按要求避免此问题。

建议的解决方法基本上是从cache 获取流。 还: here 和 here

但这意味着如果还想从列表中的卡片提供导航,则不能让 StreamBuilder 构建列表不断更新。例如卡片onPressed()。 See here.

所以刷新数据必须使用pull来刷新……

谁有更好的解决方案? 或者 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) 

在 null 上调用了 getter 'stream'。

调用了方法“add” 空。

【问题讨论】:

你在寻求什么帮助?真正的问题是什么? @FilledStacks 问题:拉刷新是实现此功能的唯一方法吗? 你到底想达到什么目的?你的目标是什么?您想在何时何地使用NavigatorState#push() 方法? @pskink 需要在 onPressed() 中调用 Navigator.push 在卡片列表中,其中 StreamBuilder 通过返回 ListView.builder 构建 @HeavenOSK 我已经添加代码 【参考方案1】:

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

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

更新:使用伪示例添加代码

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();
      
    );
  

【讨论】:

我的直播来自 Firestore,所以我认为你不可能回答 @FlutterFirebase 这绝对是可能的。在您的状态视图中本地订阅颤振流,转换并发出通过您状态的本地流的值。在小部件构建器中使用本地流,而不是 firestore 状态。当您离开时,取消您设置的本地流的订阅。那么你就不会再得到任何值了。 感谢您的回复!我现在尝试实现您的缓存,但无法正常工作。我的代码有更新问题 @FlutterFirebase 这不是缓存实现,而且您不需要缓存任何内容。您只需在按原样使用 firebase 流之间添加一个步骤:/ 现在没有时间输入代码。稍后我会回到这个问题并编写伪代码来帮助您了解如何解决它。 @FlutterFirebase 这是我现在可以用代码做的最好的事情。我今天必须发布一个应用程序。这都是伪代码,但应该让你知道该怎么做。【参考方案2】:

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

即使你完全关闭你的应用程序,运行查询也应该缓存它(我相信只在完全关闭的情况下缓存它长达 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更新时,它会更新较低的小部件。

现在这样,当您在较低的小部件中点击卡片进行导航时,它不应影响最高的小部件,因为它只会影响 UI。

我们将流构建器放在它自己的***小部件中...

我希望我有一些意义:(

我在没有测试的情况下编写了代码,但我确信你可以让它工作

【讨论】:

以上是关于StreamBuilder 限制的主要内容,如果未能解决你的问题,请参考以下文章

在java中,Stream.Builder线程是否安全?

StreamBuilder 限制

过滤 Streambuilder/listviewBuilder 颤动

使用 StreamBuilder 而不是 FeatureBuilder 来避免 Firestore 中的 whereIn 10 限制

Stream Builder 在 Flutter 中的先前数据之上更新数据

Flutter ListView.builder在滚动时结结巴巴并跳到顶部[重复]