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 限制的主要内容,如果未能解决你的问题,请参考以下文章
过滤 Streambuilder/listviewBuilder 颤动
使用 StreamBuilder 而不是 FeatureBuilder 来避免 Firestore 中的 whereIn 10 限制