I am trying to implement stream builder with cloud firestore to retrieve field data.
Here is the code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test builder"),
),
body: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('joystick').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data?.docs.length,
itemBuilder: (context, i){
QueryDocumentSnapshot<Object?>? ds = snapshot.data?.docs[i];
return Text("$snapshot.data?.docs[i].data()!['call']");
});
}
),
);
}
However, it does not output the actual data stored in the database.
I get the following output:
AsyncSnapshot<QuerySnapshot<Object?
>>(ConnectionState.active,Instance of '_JsonQuerySnapshot',null, null).data?.docs[i].data()!['call']
What should I do to get the data stored in the database? (The field name is 'call')
In order to implement stream builder with cloud firestore to retrieve field data, you can follow the StreamBuilder Documentation and link related to explanation of streambuilder in flutter.
As mentioned the thread, you can go through the following code on retrieving data through streambuilder :
body: new StreamBuilder(
stream: Firestore.instance.collection("collection").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text( 'No Data...', );
}
else {
<DocumentSnapshot> items = snapshot.data.documents;
return new Lost_Card(
headImageAssetPath : items[0]["url"] );
}
If you want to create list builder from many documents use it like
this
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.documents[index];
return new Lost_Card(
headImageAssetPath : ds["url"];
);
For more information, you can refer to the Documentation where real time updates with cloud firestore have been explained.
Try this one to return your data
return Text(ds['enterYourDataFieldName']);
What you are trying to achieve concerns RealTime database, Firestore database is not made for that.
Related
I have a streamBuilder where the "snapshots()" change from Stream<QuerySnapshot<Map<String, dynamic>>> to AsyncSnapshot<Object?>.
This is weird because I have another app, with identical code and works fine.
The problem is, I cannot access "snapshot.data.docs" and all firestore datas.
Why is happening?
my code:
body: StreamBuilder(
//Stream<QuerySnapshot<Map<String, dynamic>>>
stream: firestore.collection('Books').orderBy('name').snapshots(),
//AsyncSnapshot<Object?>
builder: (context, snapshot ) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
{
final document = snapshot.data?; //error here
return ListView.builder(
itemCount: document?.length,
itemBuilder: (context, index) {
return Column(
Declare your StreamBuilder Widget response type as a dynamic, and you should use
snapshot.data.docs without any problem.
body: StreamBuilder<dynamic>(
stream: FirebaseFirestore.instance.collection('Books').orderBy('name').snapshots(),
builder: (context, snapshots) {
if(snapshots.hasData){
print(snapshots.data.docs.toString());
}});
I am trying to request in every tab to Firebase Firestore. Still, if I do not add a listener to the tabController with setState((){}) it does not refresh the view and the setting state adds to the screen's momentary rerendering effect. I want to avoid this effect. Thanks for all answers.
Firestore have an awesome snapshot build right into it. you can combine that with StreamBuilder from Flutter.Streambuilder will listen to the changes from Firestore using snapshot. you don't need to worry about the state (you can use stateless) as Streambuilder with auto rebuild every time the data on your Firestore update.
return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: db.collection('drugs').snapshots(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (!snapshot.hasData) {
return const Center(
child: Text('No Drug'),
);
} else {
final list = snapshot.data!.docs
.map((e) => Drug.fromMap(e.data()))
.toList();
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
return ProductTile(list[index]);
},
);
}
})),
DatabaseService databaseService = new DatabaseService();
Stream questionsSnapshot;
so Im using a stream and and a database service to retrieve data (questions and answers of a quiz) to my listView builder
the called function from database service is
getAquizData(String quizId) async{
return await Firestore.instance
.collection("quiz")
.document(quizId)
.collection("questionReponses")
.snapshots();
}
the init state function
#override
void initState() {
databaseService.getAquizData(widget.quizId).then((value){
questionsSnapshot = value;
setState(() {});
});
super.initState();
}
my listViewBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
// an appbar
) ,
body: Container(
child: Column(children: <Widget>[
StreamBuilder(
stream: questionsSnapshot,
builder: (context, snapshot) {
return snapshot.data == null
? Container(child: Text("empty"),) : ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: snapshot.data.documents.length,
itemBuilder: (context,index){
return QuizPlayTile(
questionModel: getQuestionModelFromDatasnapshot(
snapshot.data.documents[index]),
index: index,
);
});
},
)
],)
)
);
}
}
when running it just show the word empty for a second and then it shows questions without answers
[1]: https://i.stack.imgur.com/E4CS4.jpg
We can use stream builder without calling it in initState method. Following code works for me. user quizStreamer in build method.
quizStreamer(){
return StreamBuilder(
stream: Firestore.instance
.collection('quiz')
.document(quizId)
.collection('questionReponses')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
//loadWidgets method takes snapshot and render defined widgets
return loadWidgets(snapshot);
},
);
}
When I update the database in console or app, the changes are not synced in the app. I thought it's because of orderBy and indexing settings, but commenting them didn't help. What's wrong with my code? How to set up auto-sync?
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: DrawerMenu(),
appBar: AppBar(
title: Text('Заявки'),
),
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('orders')
.where('companyId', isEqualTo: globals.companyId)
// .orderBy('dateAdded', descending: true)
// .orderBy('statusId')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return LoadingScreen();
default:
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) => GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Items(
order: snapshot.data.documents[index],
),
)),
child: OrderCard(
order: snapshot.data.documents[index],
)));
}
},
),
);
}
Its because you are just getting the snapshot as a stream once, but to get the actual changes when you modify in console or app you need to make a subscription to the stream. You need to .listen the stream of data so that you get notified when something change.
Also you may want to separate the logic of firebase its easier.
For example:
Create a Stream Controller:
final StreamController<DataType> _streamController = StreamController<DataType>();
Listen to firebase data and add it to stream:
Firestore.instance
.collection('orders')
.where('companyId', isEqualTo: globals.companyId)
.snapshots().listen((DocumentSnapshot snapShot){
_streamController.add(snapShot.documents);
});
Now you can listen to the stream from stream controller:
_streamController.stream;
It turned out that it's necessary to add custom indexes in firestore console when a query consists of .where().orderBy and so on.
I am fetching articles from HackerNews API using Bloc Pattern and Streams.
I am loading all the articles and presenting in the UI with the help of a stream builder, and this works fine.
Now I wrapped the article fetching Stream builder with the new loading StreamBuilder.
Now when the loading stream builder has true (means it is loading) it shows a circular progress indicator or else, it shows the child (Article List wrapped with a Stream Builder).
This works fine. But it is bugging me that I have wrapped Stream builder inside a stream builder. I know I can take help of rxdart but I am just not sure how.
I tried to add a loader with the help of snapshot.hasData or not but that didn't work, so I decided to create another stream and subject that takes a bool and tells the UI if it is loading or not.
Code fetching data int the bloc:
_getAndUpdateArticles(StoryType storyType) {
_isLoadingSubject.add(true);
getListIds(storyType).then((list) {
getArticles(list.sublist(0, 10)).then((_){
_articleSubject.add(UnmodifiableListView(_articles));
_isLoadingSubject.add(false);
});
});
}
UI:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: StreamBuilder(
stream: widget.hnBloc.isLoading,
builder: (context, snapshot) {
if (snapshot.data) {
return Center(child: CircularProgressIndicator());
} else {
return StreamBuilder<UnmodifiableListView<Article>> (
initialData: UnmodifiableListView<Article>([]),
stream: widget.hnBloc.article,
builder: (context, snapshot) => ListView(
children: snapshot.data.map(_buildItem).toList(),
),
);
}
},
),
.........
EDIT
I have tried this, but this isn't working:
StreamBuilder<UnmodifiableListView<Article>> (
initialData: UnmodifiableListView<Article>([]),
stream: widget.hnBloc.article,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: snapshot.data.map(_buildItem).toList(),
);
} else {
return CircularProgressIndicator();
}
}
),
I Don't think there is a complete way to avoid nested StreamBuilders. I personally wouldn't consider it a bad practice, but it will definitely lead to more build.
In your case, You can modify your hnBloc to emit a single state that can be a loading state or data state , thereby eliminating the need for a nested StreamBuider.
eg.
StreamBuilder<HnState>(
stream: hnBloc.currentState,
initialData: HnLoadingState(),
builder: (context, snapshot) {
if (snapshot.data is HnLoadingState) {
return Center(child: CircularProgressIndicator());
}if (snapshot.data is HnDataState) {
return ListView(
children: snapshot.data.map(_buildItem).toList(),
),
}
},
)
This pattern is very common when using the flutter_bloc package. You can see a basic example of this here to understand it better.