how can I show document? - flutter

I am trying to fetch document one by one ...i am using below code its work but in console its show me error like
StreamBuilderBaseState>#de08b):
The getter 'documents' was called on null.
Receiver: null
Tried calling: documents
new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Quiz").where("topice",isEqualTo: widget.topic).where("section",isEqualTo: widget.section).snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot,) {
int length= snapshot.data.documents.length;
if (!snapshot.hasData)
return new Container(child: Text(""),);
return ListView(
children: <Widget>[
Text(widget.scoren.toString()),
Text(snapshot.data.documents[cunter]["Description"]),
Row(
children: <Widget>[
CupertinoButton(child: Text("COMMENTS"), onPressed: null),
RaisedButton(onPressed: (){
setState(() {
cunter++;
});
// Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>new MyApp()));
},child: Text("NEXT"),)
],
)
],
)

You are trying to access the length property of the documents list before you check if the snapshot has any data.
Try inverting those lines, like this
//Check if the snapshot has data
if (!snapshot.hasData) return Container(child: Text(""));
//If you get here it means you have data
int length= snapshot.data.documents.length;
Also, remember that in Dart 2 the new keyworkd is not needed anymore.

Related

reading data from firebase firestore collection at stream builder

I got trouble with firebase fireStore.
There is a stream builder reading data from items collection.
Inside items collection there is some fields and another collections.
I haven't any problem with fields, the problem is with collection.
how to access those collections inside stream builder?
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: CallApi().finalReference(reference: widget.finalReference),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('snapshot Error:${snapshot.error}'));
}
if (snapshot.hasData) {
var snapData = snapshot.data!.docs;
if (kDebugMode) {
print(snapData.length);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.builder(
itemCount: snapData.length,
itemBuilder: (BuildContext context, int index) {
return ListItem(
mTitle: snapData[index].get('title') ?? '',
mSubTitle: snapData[index].get('address') ?? 'empty',
mPrice: snapData[index].get('price') ?? '',
mImageUrl: snapData[index].get('gallery')[0],
mOnTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
adsTitle: snapData[index].get('title'),
adsSubTitle: snapData[index].get('subTitle'),
gallery: snapData[index].get('gallery'),
specFTitle: snapData[index].get('gallery'),
),
),
);
},
);
},
),
),
],
);
}
return const Center(child: CircularProgressIndicator());
},
),
here is firebase
Reading data from Firestore is a shallow operation. When you read a document, its subcollection are not automatically read.
So if you want to get the data from the subcollections of the current document, you will have to start a new read operation for that. If you want to show that data in the UI, you can use a new, nested StreamBuilder or FutureBuilder for that.

(Flutter) StreamBuilder returns only null

I am trying to create a "CategoryStream" to update the UI based on the users choice.
This is my stream:
import 'dart:async';
class CategoryStream {
StreamController<String> _categoryStreamController =
StreamController<String>();
closeStream() {
_categoryStreamController.close();
}
updateCategory(String category) {
print("Update category in stream was called");
print("the new category is: " + category);
_categoryStreamController.sink.add(category);
}
Stream<String> get stream => _categoryStreamController.stream;
}
And my StreamBuilder looks like this:
return StreamBuilder<String>(
stream: CategoryStream().stream,
builder: (context, snapshot) {
return Container(
color: Colors.white,
child: Center(
child: Text(snapshot.data.toString()),
),
);
},
);
So when the User choses a new category, i try to update the Stream like this:
CategoryStream().updateCategory(currentChosenCategory);
Whatever i do, the result is always null. Although the right category is displayed in the print() function...
What am i missing?
Maybe i need to work with a StreamProvider? Not a StreamBuilder? Because i am adding the data from a Parent-Widget to a Child-Widget of a Child-Widget..
By default, the value of a StreamController is null. You need to set at initialData or add data to the stream before you call snapshot.data in your StreamBuilder:
final CategoryStream _categoryStream = CategoryStream();
return StreamBuilder<String>(
stream: _categoryStream.stream,
initialData: "",
builder: (context, snapshot) {
return Container(
color: Colors.white,
child: Center(
child: Text(snapshot.data), //toString() is unnecessary, your data is already a string
),
);
},
);

Flutter widgets, how to use multiple async Future,. data loading questions

I have a basic app, loading data into a list view widget, I have a 2nd set of data I'd like to be able to reference
This kind of thing, but in the creation of the List Tile, I want to use data from another method called getOtherData() .. essentially to join the data but I'd rather not do it in sql/object creation..
child: FutureBuilder<List<Contact>>(
future: getContacts(),
builder: (BuildContext context,
AsyncSnapshot<List<Contact>> snapshot) {
return ListView(
children: snapshot.hasData
? snapshot.data
.map((e) =>[ ListTile(
leading: ExcludeSemantics(
child: CircleAvatar(
child: Text(e.daysSinceContacted
.toString()),
)),
title: Text(e.firstName),
subtitle: Text(DateTime.now()
.difference(e.lastContacted)
.inDays
.toString() +
" Days" +
":" +
e.lastContacted
.toIso8601String() +
getGroupName(e, groups)
.whenComplete((x) => x)),
enabled: true,
},
))))
.toList()
: []);
})),
So where I do
title: Text(e.firstName),
I'd like to do
title: Text(e.firstName + getOtherData(e.id)
I can't figure out how to deal with the Future<> returns from the db sync methods though...
basically, just how do I get data so that I can use it?
The easiest way might be to use another FutureBuilder.
title: FutureBuilder<String>(
future: getOtherData(e.id),
builder: (_, snapshot) {
if(!snapshot.hasData) return Container(); // or return something else while loading
final otherData = snapshot.data;
return Text(e.firstName + otherData);
},
),

How to get a document snapshot index

I'm new to flutter and I'm trying to pass a firestore document snapshot to another class.
I passed to the Profile class a snapshot document, and I want to indicate the index of my document, but I don't know how to get it
I have this
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: ((searchString != null) &&
(searchString.trim() != ""))
? Firestore.instance
.collection('pazienti')
.where("searchIndex",
arrayContains: searchString)
.snapshots()
: Firestore.instance
.collection('pazienti')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
return ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return Card(
elevation: 10.00,
margin: EdgeInsets.all(0.50),
child: ListTile(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[????])));
}
,
leading: CircleAvatar(
backgroundColor:
Colors.blueGrey.shade800,
),
title: Text(document['cognome'] +
" " +
document['nome']),
subtitle: Text(document['cognome'] +
" " +
document['nome']),
),
);
}).toList(),
);
}
})),
],
),
)
My problem is essentially here
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile(miaquery: snapshot.data.documents[XXXX]))
How can I get the index of the document from the map I used?
Thank you very much for your help
You just want to pass document on which tap, so you can simply pass document which you are getting from map method.
Snapshots from a query don't have a numeric index. The results from a query could change at any time between queries, and the system can not guarantee that any sort of index would be stable.
If you want to pass a document to another function, pass its unique document ID. The receiver can then query the document directly, perhaps from local cache without requiring a billed read operation at the server.
var listIndex= snapshot.data.documents.map((e) => e.data['key']);
var passIndex = listIndex.toList().indexOf(doc.data['key']);
print(passIndex);
You can simply pass the index when assigning the list
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildItem(context, data, snapshot.indexOf(data))).toList(),
);
}

I am getting error while using stream builder in flutter

i am making a mobile app using flutter. And i am using stream builder for this screen. I am not getting the point where i am wrong in the code. Can you please help me in this. I am sharing code and screenshot for this particular row which is causing problem
var timeSelected = 'Click here';
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Time Slot:',
style: TextStyle(color: Colors.white),
),
Spacer(),
GestureDetector(
onTap: () {
_asyncInputDialog(context);
//_displayDialog();
},
child: StreamBuilder(stream: cartManager.getTimeSlotSelected,
initialData: timeSelected,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData){
timeShow(snapshot,);
}
else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: Container(
child: Text('Select time slot'),
),
);
},)
),
],
),
This alert dialog will show when i click on the text of row:
_asyncInputDialog(
BuildContext context,
) {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Center(child: Text('Available Time Slot')),
content: TEAlertDialogContent(),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
When i got the value from showdialog i will store the value in streamcontroller that is present in CartManager.
static StreamController<Timeslot> timeSlotController = BehaviorSubject();
timeSlotSelected(Timeslot time){
timeSlotController.sink.add(time);
}
get getTimeSlotSelected{
return timeSlotController.stream;
}
And we call the above method in stream property of streamcontroller and get the snapshot. This is the method which was called when our snapshot has data:
Widget timeShow(AsyncSnapshot<Timeslot> snapshot ) {
timeSelected = '${snapshot.data.firstTimeSlot}-${snapshot.data.secondTimeSlot}';
timeslotid = snapshot.data.id.toString();
return Text(timeSelected);
}
But i am getting error: type 'BehaviorSubject' is not a subtype of type 'Stream'
Please let me know where i am wrong. I had also shared a screen shot of screen showing this error too.
As your error states, you are trying to pass a type Timeslot to a Stream builder expecting a stream of type String. You must check which one is correct (String or Timeslot) and use the same type on both sides.
Apparently, your problem is in the timeSelected variable. Where is it defined? If this is a String, the Stream builder will infer that your stream is of type String, which is not true. You must set this variable as a Timeslot, since this is your stream type.
Also, you have an error in your code. You have to return a widget to be rendered if snapshot has data. Check the code below:
StreamBuilder(stream: cartManager.getTimeSlotSelected,
initialData: timeSelected,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData){
return timeShow(snapshot,);
}
else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: Container(
child: Text('Select time slot'),
),
);
},)