Dart StreamBuilder限制
Dart StreamBuilder限制,dart,flutter,future,stream-builder,flutter-navigation,Dart,Flutter,Future,Stream Builder,Flutter Navigation,StreamBuilder在获得新事件时重新生成。这会导致例如导航(Navigator.push)出现问题,因为如果在导航时接收到新事件,则此触发器将重新生成。因为在小部件树仍在构建时尝试导航,这将抛出 应根据需要避免此问题 建议的解决方法基本上是从中获取流。 也: 和 但这意味着,如果还想从列表中的卡片提供导航,则不能让StreamBuilder构建列表不断更新。例如,在卡片onPressed()中 所以要刷新数据,必须使用pull来刷新 谁有更好的解决办法? 或者,Flatter团队是否正
StreamBuilder
在获得新事件时重新生成。这会导致例如导航(Navigator.push
)出现问题,因为如果在导航时接收到新事件,则此触发器将重新生成。因为在小部件树仍在构建时尝试导航,这将抛出
应根据需要避免此问题
建议的解决方法基本上是从中获取流。
也:
和
但这意味着,如果还想从列表中的卡片提供导航,则不能让StreamBuilder构建列表不断更新。例如,在卡片onPressed()
中
所以要刷新数据,必须使用pull来刷新
谁有更好的解决办法?
或者,Flatter团队是否正在解决此限制,例如,如果用户点击卡,是否允许防止重建
更新:
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();
}
更新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);
}
更新4:
建议使用\u 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) {
StreamController\u localStreamController=StreamController();
@凌驾
void initState(){
super.initState();
Firestore.instance.collection('info').snapshot().listen((QuerySnapshot QuerySnapshot){
//if(userAdded==null){
_localStreamController.add(querySnapshot);
// }
});
...
孩子:StreamBuilder(
流:_localStreamController.stream,
生成器:(上下文,快照){
对null调用了getter“stream”
已对调用方法“add”
空
根据您上面的评论,实际问题似乎是,在您使用流导航离开视图后,它会崩溃。您必须:
- 当您离开时取消流控制器,使其不再侦听任何事件
- 或者只是在导航后不通过流发出任何新值。在返回视图之前在流上添加暂停
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();
}
);
}
}
类小部件{
//您的本地流
Stream\u localStream;
//值以指示是否已导航离开
bool hasNavigated=false;
...
void init(){
//订阅firebase流
firebaseStream…侦听((值){
//如果此值仍然为false,则向localStream发出相同的值
如果(!已导航){
_localStream.add(value);
}
});
}
小部件构建(){
返回流生成器(
//订阅本地流,而不是firebase流
流:_localStream,
//和以前一样处理
生成器:(上下文,快照){
返回您的widgets();
}
);
}
}
尝试将所有内容分解为小部件
即使完全关闭应用程序,运行查询也应该缓存它(我相信只在完全关闭时缓存它最多30分钟,但如果您没有internet连接,您仍然可以从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
}
);
}
@覆盖
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(标题:文本(“请工作”),
正文:_buildStream(上下文),
);
}
Widget\u buildStream(BuildContext上下文){
返回流生成器(
stream:yourFireStoreStream,
生成器:(上下文,快照){
如果(!snapshot.hasData)返回LinearProgressIndicator();
return\u buildAnotherwidget(上下文、快照、数据、文档);
},
);
}
Widget\u buildAnotherwidget(Buildcontext上下文,列表快照){
返回ListView.Builder(
itemCount:snaps.length,
itemBuilder:(上下文,索引){
…在这里显示您的卡片等…或构建另一个小部件来显示卡片
}
);
}
专注于分解更多的小部件。最高部分应该有streambuilder和流。然后深入到更多的小部件。
streambuilder将自动侦听并订阅给定流
当streambuilder更新时,它将更新较低的小部件
现在这样,当您点击较低窗口小部件中的卡片进行导航时,它不应该影响最高窗口小部件,因为它只会影响UI
我们将streambuilder放在它自己的顶级小部件中
我希望我说得有道理:(
我在没有测试的情况下编写了代码,但我相信你可以让它正常工作你在寻求什么帮助?实际问题是什么?@FilledStacks问题:拉刷新是实现此功能的唯一方法吗?你到底想实现什么?你的目标是什么?你想在哪里和什么时候使用
导航状态#push()
method?@pskink需要调用Navigator。按下onPressed()中的
在卡片列表中,其中StreamBuilder
buildbyreturnListView.builder
@HeavenOSK我添加了代码我的流来自Firestore,所以我想你的答案不是possible@FlutterFirebase这绝对是可能的。在您的状态视图中本地订阅长笛流,转换并发出通过l的值您所在状态的本地流。使用widget builder中的本地流,而不是firestore状态。当您导航离开时,取消对您设置的本地流的订阅。然后您将不会获得任何更多值。感谢您的回复!我正在尝试实现您的缓存,但无法正常工作。我有更新问题code@FlutterFirebase那不是它不是一个缓存实现,而且,您不需要缓存任何东西。您只需在使用firebase流原样之间添加一个步骤即可:/n现在没有时间键入代码。我稍后将回到这一点,并编写伪代码以提供帮助
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();
}
);
}
}
@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
}
);
}