cloud_firestore文档中的此示例使用StreamBuilder
中的ConnectionState
和AsyncSnapshot
来处理处于不同状态的流。通过ConnectionState
而非StreamProvider
访问流时,是否有类似的方法来管理StreamBuilder
?避免它在实际上有来自Firestore的文档之前短时间内返回null
的最佳方法是什么?
这是带有StreamBuilder的cloud_firestore文档中的示例:
class BookList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']),
);
}).toList(),
);
}
},
);
}
}
我有一个相当基本的信息流:
List<AuditMark> _auditMarksFromSnapshot(QuerySnapshot qs) {
return qs.documents.map((DocumentSnapshot ds) {
return AuditMark.fromSnapshot(ds);
}).toList();
}
Stream<List<AuditMark>> get auditMarks {
return Firestore.instance
.collection('_auditMarks')
.snapshots()
.map(_auditMarksFromSnapshot);
}
可通过StreamProvider
访问此地址(此处省略了其他提供程序):
void main() async {
runApp(MultiProvider(
providers: [
StreamProvider<List<AuditMark>>(
create: (_) => DatabaseService().auditMarks, ),
],
child: MyApp(),
));
}
我曾尝试以某种方式将QuerySnapshot
转换为AsyncSnapshot<QuerySnapshot>
,但可能会出错。
当然可以像这样给StreamProvider
一些initialData
-但这很麻烦,容易出错并且可能很昂贵:
initialData: <AuditMark>[
AuditMark.fromSnapshot(await Firestore.instance
.collection('_auditMarks')
.orderBy('value')
.getDocuments()
.then((value) => value.documents.first))
...但是我希望有一种更聪明的方法来管理连接状态,并避免在发送文档之前返回空值?
答案 0 :(得分:1)
我一直在处理此问题,不想声明initialData
来绕过此问题。
我所做的是创建一个StreamBuilder作为StreamProvider的子 。
这样我就可以在StreamProvider的中使用StreamBuilder的snapshot.connectionState
属性。
这是代码:
return StreamProvider<List<AuditMark>>.value(
value: DatabaseService().auditMarks,
child: StreamBuilder<List<AuditMark>>(
stream: DatabaseService().auditMarks,
builder: (context, snapshot) {
if (!snapshot.hasError) {
switch (snapshot.connectionState) {
case ConnectionState.none: // if no connection
return new Text(
"Offline!",
style: TextStyle(fontSize: 24, color: Colors.red),
textAlign: TextAlign.center,
);
case ConnectionState.waiting
// while waiting the data, this is where you'll avoid NULL
return Center(child: CircularProgressIndicator());
default:
return ListView.builder(
// in my case I was getting NULL for itemCount
itemCount: logs.length,
itemBuilder: (context, index) {
return LogsTile(log: logs[index]);
},
);
}
}
else {
return new Text(
"Error: ${snapshot.error}",
style: TextStyle(fontSize: 17, color: Colors.red),
textAlign: TextAlign.center,
);
}
}
)
);
答案 1 :(得分:0)
可能不是最优雅的解决方案,但是我最终使用了一个简单的bool变量,虽然并非所有StreamProvider都发出了值,但它是正确的。
bool _waitForStreams = false;
if (Provider.of<List<AuditMark>>(context) == null) _waitForStreams = true;
if (Provider.of<...>>(context) == null) _waitForStreams = true;
(etc. repeat for every StreamProvider)
// show "loading..." message while not all StreamProviders have supplied values
if (_waitForStreams) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 25.0),
Text('loading...'),
],
)
],
),
);
}
答案 2 :(得分:0)
我不知道它是否正确,但这就是我的实现方式。
由于streamProviser
不提供连接状态,因此我首先使用streamBuilder
,然后使用provider.value
分发数据:
return StreamBuilder<BusinessM>(
stream: db.businessDetails(), //firebase stream mapped to business model class
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active)
return Provider<BusinessM>.value(
value: snapshot.data,
child: Businesspage(),
);
else
return Center(child: CircularProgressIndicator());
});